From 60921dcc76769f24638daf512a02dedbf8d7620c Mon Sep 17 00:00:00 2001 From: Avraham Sakal Date: Mon, 26 Jun 2023 00:08:48 -0400 Subject: [PATCH] cleaner pattern --- dist/index.js | 120 +++++++++++++++++++++++++------------------------ src/App.tsx | 52 +++++++++------------ src/Picker.tsx | 90 ++++++++++++++++++++----------------- 3 files changed, 133 insertions(+), 129 deletions(-) diff --git a/dist/index.js b/dist/index.js index abb994a..f5208b5 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1116,7 +1116,7 @@ var require_react_development = __commonJS({ var dispatcher = resolveDispatcher(); return dispatcher.useCallback(callback2, deps); } - function useMemo(create, deps) { + function useMemo2(create, deps) { var dispatcher = resolveDispatcher(); return dispatcher.useMemo(create, deps); } @@ -1887,7 +1887,7 @@ var require_react_development = __commonJS({ exports.useImperativeHandle = useImperativeHandle; exports.useInsertionEffect = useInsertionEffect; exports.useLayoutEffect = useLayoutEffect; - exports.useMemo = useMemo; + exports.useMemo = useMemo2; exports.useReducer = useReducer2; exports.useRef = useRef3; exports.useState = useState; @@ -36589,6 +36589,22 @@ var useStore = (options) => { const store = (0, import_react2.useContext)(StoreContext); return (options == null ? void 0 : options.store) || store || getDefaultStore(); }; +var Provider = ({ + children, + store +}) => { + const storeRef = (0, import_react2.useRef)(); + if (!store && !storeRef.current) { + storeRef.current = createStore(); + } + return (0, import_react2.createElement)( + StoreContext.Provider, + { + value: store || storeRef.current + }, + children + ); +}; var isPromiseLike2 = (x) => typeof (x == null ? void 0 : x.then) === "function"; var use = import_react2.default.use || ((promise) => { if (promise.status === "pending") { @@ -36668,36 +36684,33 @@ function useAtom(atom2, options) { // src/Picker.tsx var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1); -function create$Picker({ $options = atom([]), $isLoading = atom(false), $selectedOption = atom(""), $url = atom(""), $isEnabled = atom(true) }) { - return atom({ - $options, - $isLoading, - $selectedOption, - $url, - Picker: () => { - const [url, setUrl] = useAtom($url); - const [options, setOptions2] = useAtom($options); - const [isLoading, setIsLoading] = useAtom($isLoading); - const [selectedOption, setSelectedOption] = useAtom($selectedOption); - const [isEnabled, setIsEnabled] = useAtom($isEnabled); - (0, import_react3.useEffect)(() => { - if (isEnabled) { - fetch(url).then((x) => x.json()).catch((err) => ["AAPL", "MSFT", "GOOG"]).then((underlyings_) => { - setOptions2(underlyings_); - setIsLoading(false); - }); - } - }, [isEnabled, url]); - return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Loading..." }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { value: selectedOption, onChange: (e) => { - setSelectedOption(e.target.value); - }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "" }, ""), - options.map( - (date) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: date, children: date }, date) - ) - ] }) }); - } - }); +function Picker({ $options, $isLoading, $url, $isEnabled, $selectedOption }) { + $options = $options || (0, import_react3.useMemo)(() => atom([]), []); + $isLoading = $isLoading || (0, import_react3.useMemo)(() => atom(true), []); + $isEnabled = $isEnabled || (0, import_react3.useMemo)(() => atom(true), []); + $selectedOption = $selectedOption || (0, import_react3.useMemo)(() => atom(""), []); + const url = useAtomValue($url); + const options = useAtomValue($options); + const isLoading = useAtomValue($isLoading); + const [selectedOption, setSelectedOption] = useAtom($selectedOption); + const isEnabled = useAtomValue($isEnabled); + const optionsFetched = useSetAtom((0, import_react3.useMemo)(() => atom(null, (get, set2, options2) => { + set2($options, options2); + set2($isLoading, false); + }), [$options, $isLoading])); + (0, import_react3.useEffect)(() => { + if (isEnabled) { + fetch(url).then((x) => x.json()).catch((err) => ["AAPL", "MSFT", "GOOG"]).then(optionsFetched); + } + }, [url, isEnabled]); + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Provider, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Loading..." }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { value: selectedOption, onChange: (e) => { + setSelectedOption(e.target.value); + }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "" }, ""), + options.map( + (date) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: date, children: date }, date) + ) + ] }) }) }); } // src/App.module.css @@ -36710,50 +36723,39 @@ var App_module_default = { var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1); var baseUrl = "http://127.0.0.1:8234"; var $underlyingsUrl = atom(`${baseUrl}/option_quotes/underlyings`); -var $underlyingPicker = create$Picker({ $url: $underlyingsUrl }); -var $quoteDatePicker = create$Picker({ - $url: atom((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/quote_dates`), - $isEnabled: atom((get) => get(get($underlyingPicker).$selectedOption) !== "") -}); -var $strikePicker = create$Picker({ - $url: atom((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/${get(get($quoteDatePicker).$selectedOption)}/strikes`), - $isEnabled: atom((get) => get(get($quoteDatePicker).$selectedOption) !== "" && get(get($underlyingPicker).$selectedOption) !== "") -}); -var $frontMonthPicker = create$Picker({ - $url: atom((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/${get(get($quoteDatePicker).$selectedOption)}/expirations`), - $isEnabled: atom((get) => get(get($quoteDatePicker).$selectedOption) !== "" && get(get($underlyingPicker).$selectedOption) !== "") -}); -var $backMonthPicker = create$Picker({ - $url: atom((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/${get(get($quoteDatePicker).$selectedOption)}/expirations`), - $isEnabled: atom((get) => get(get($quoteDatePicker).$selectedOption) !== "" && get(get($underlyingPicker).$selectedOption) !== "") -}); +var $selectedUnderlying = atom(""); +var $quoteDatePickerUrl = atom((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/quote_dates`); +var $isQuoteDatePickerEnabled = atom((get) => get($selectedUnderlying) !== ""); +var $selectedQuoteDate = atom(""); +var $strikePickerUrl = atom((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/strikes`); +var $isStrikePickerEnabled = atom((get) => get($selectedQuoteDate) !== "" && get($selectedUnderlying) !== ""); +var $selectedStrike = atom(""); +var $frontMonthExpirationPickerUrl = atom((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/expirations`); +var $isFrontMonthExpirationPickerEnabled = atom((get) => get($selectedQuoteDate) !== "" && get($selectedUnderlying) !== ""); +var $backMonthExpirationPickerUrl = atom((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/expirations`); +var $isBackMonthExpirationPickerEnabled = atom((get) => get($selectedQuoteDate) !== "" && get($selectedUnderlying) !== ""); function App() { - const { Picker: UnderlyingPicker } = useAtomValue($underlyingPicker); - const { Picker: QuoteDatePicker } = useAtomValue($quoteDatePicker); - const { Picker: StrikePicker } = useAtomValue($frontMonthPicker); - const { Picker: FrontMonthPicker } = useAtomValue($frontMonthPicker); - const { Picker: BackMonthPicker } = useAtomValue($backMonthPicker); return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: App_module_default.app, children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Header_default, {}), /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: App_module_default.picker, children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { children: "Underlying" }), - /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UnderlyingPicker, {}) + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Picker, { $url: $underlyingsUrl, $selectedOption: $selectedUnderlying }) ] }), /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: App_module_default.picker, children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { children: "Quote Date" }), - /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(QuoteDatePicker, {}) + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Picker, { $url: $quoteDatePickerUrl, $isEnabled: $isQuoteDatePickerEnabled, $selectedOption: $selectedQuoteDate }) ] }), /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: App_module_default.picker, children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { children: "Strike" }), - /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StrikePicker, {}) + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Picker, { $url: $strikePickerUrl, $isEnabled: $isStrikePickerEnabled, $selectedOption: $selectedStrike }) ] }), /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: App_module_default.picker, children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { children: "Front Expiration" }), - /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FrontMonthPicker, {}) + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Picker, { $url: $frontMonthExpirationPickerUrl, $isEnabled: $isFrontMonthExpirationPickerEnabled }) ] }), /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: App_module_default.picker, children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { children: "Back Expiration" }), - /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BackMonthPicker, {}) + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Picker, { $url: $backMonthExpirationPickerUrl, $isEnabled: $isBackMonthExpirationPickerEnabled }) ] }), /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HistoricalImpliedVolatilityChart, {}) ] }); diff --git a/src/App.tsx b/src/App.tsx index d8e1992..9dad557 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import Header from './Header'; import { HistoricalImpliedVolatilityChart } from "./HistoricalImpliedVolatilityChart"; -import { create$Picker } from './Picker'; -import { atom as $, useAtomValue } from 'jotai'; +import { Picker } from './Picker'; +import { atom as $ } from 'jotai'; //import './index.css'; //@ts-ignore import k from './App.module.css'; @@ -10,39 +10,31 @@ const baseUrl = 'http://127.0.0.1:8234'; /* The following are wrapped in atoms to prevent re-creating them if App() re-runs. */ const $underlyingsUrl = $(`${baseUrl}/option_quotes/underlyings`); -const $underlyingPicker = create$Picker({$url:$underlyingsUrl}); -const $quoteDatePicker = create$Picker({ - $url: $((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/quote_dates`), - $isEnabled: $((get) => get(get($underlyingPicker).$selectedOption)!=='') -}); -const $strikePicker = create$Picker({ - $url: $((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/${get(get($quoteDatePicker).$selectedOption)}/strikes`), - $isEnabled: $((get) => get(get($quoteDatePicker).$selectedOption)!=='' && get(get($underlyingPicker).$selectedOption)!=='') -}); -const $frontMonthPicker = create$Picker({ - $url: $((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/${get(get($quoteDatePicker).$selectedOption)}/expirations`), - $isEnabled: $((get) => get(get($quoteDatePicker).$selectedOption)!=='' && get(get($underlyingPicker).$selectedOption)!=='') -}); -const $backMonthPicker = create$Picker({ - $url: $((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/${get(get($quoteDatePicker).$selectedOption)}/expirations`), - $isEnabled: $((get) => get(get($quoteDatePicker).$selectedOption)!=='' && get(get($underlyingPicker).$selectedOption)!=='') -}); +const $selectedUnderlying = $(''); -function App() { - const {Picker:UnderlyingPicker} = useAtomValue($underlyingPicker); - const {Picker:QuoteDatePicker} = useAtomValue($quoteDatePicker); - const {Picker:StrikePicker} = useAtomValue($frontMonthPicker); - const {Picker:FrontMonthPicker} = useAtomValue($frontMonthPicker); - const {Picker:BackMonthPicker} = useAtomValue($backMonthPicker); +const $quoteDatePickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/quote_dates`); +const $isQuoteDatePickerEnabled = $((get) => get($selectedUnderlying)!==''); +const $selectedQuoteDate = $(''); + +const $strikePickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/strikes`); +const $isStrikePickerEnabled = $((get) => get($selectedQuoteDate)!=='' && get($selectedUnderlying)!==''); +const $selectedStrike = $(''); + +const $frontMonthExpirationPickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/expirations`); +const $isFrontMonthExpirationPickerEnabled = $((get) => get($selectedQuoteDate)!=='' && get($selectedUnderlying)!==''); +const $backMonthExpirationPickerUrl = $((get) => `${baseUrl}/option_quotes/${get($selectedUnderlying)}/${get($selectedQuoteDate)}/expirations`); +const $isBackMonthExpirationPickerEnabled = $((get) => get($selectedQuoteDate)!=='' && get($selectedUnderlying)!==''); + +function App() { return (
-
-
-
-
-
+
+
+
+
+
); diff --git a/src/Picker.tsx b/src/Picker.tsx index d74a011..e638dd7 100644 --- a/src/Picker.tsx +++ b/src/Picker.tsx @@ -1,44 +1,54 @@ -import { useEffect } from "react"; -import { atom as $, useAtom, Atom, PrimitiveAtom } from 'jotai'; +import { useEffect, useMemo } from "react"; +import { atom as $, useAtom, Atom, PrimitiveAtom, useAtomValue, useSetAtom, Provider } from 'jotai'; -type PickerInput = { $options?:PrimitiveAtom>, $isLoading?:PrimitiveAtom, $selectedOption?:PrimitiveAtom, $url:Atom, $isEnabled?:Atom }; -export function create$Picker({ $options=$([]), $isLoading=$(false), $selectedOption=$(''), $url=$(''), $isEnabled=$(true) }: PickerInput){ - return $({ - $options, - $isLoading, - $selectedOption, - $url, - Picker: ()=>{ - const [url, setUrl] = useAtom($url); - const [options, setOptions] = useAtom($options); - const [isLoading, setIsLoading] = useAtom($isLoading); - const [selectedOption, setSelectedOption] = useAtom($selectedOption); - const [isEnabled, setIsEnabled] = useAtom($isEnabled); +type PickerInput = { + $options?:PrimitiveAtom>, + $isLoading?:PrimitiveAtom, + $selectedOption?:PrimitiveAtom, + $url:Atom, + $isEnabled?:Atom +}; +export function Picker({ $options, $isLoading, $url, $isEnabled, $selectedOption }: PickerInput){ + $options = $options || useMemo(()=>$([]),[]); + $isLoading = $isLoading || useMemo(()=>$(true),[]); + $isEnabled = $isEnabled || useMemo(()=>$(true),[]); + $selectedOption = $selectedOption || useMemo(()=>$(''),[]); + + const url = useAtomValue($url); + const options = useAtomValue($options); + const isLoading = useAtomValue($isLoading); + const [selectedOption, setSelectedOption] = useAtom($selectedOption); + const isEnabled = useAtomValue($isEnabled); - useEffect(()=>{ - if(isEnabled){ - fetch(url) - .then(x=>x.json()) - .catch((err)=>['AAPL', 'MSFT', 'GOOG']) - .then((underlyings_)=>{ setOptions(underlyings_); setIsLoading(false); }) - } - },[isEnabled, url]) - - return ( -
- {isLoading - ? - Loading... - : - - } -
- ); + const optionsFetched = useSetAtom(useMemo(()=>$(null, (get,set,options)=>{ + set($options, options); + set($isLoading, false); + }),[$options, $isLoading])); + + useEffect(()=>{ + if(isEnabled){ + fetch(url) + .then(x=>x.json()) + .catch((err)=>['AAPL', 'MSFT', 'GOOG']) + .then(optionsFetched) } - }); + },[url, isEnabled]) + + return ( + +
+ {isLoading + ? + Loading... + : + + } +
+
+ ); } \ No newline at end of file