cleaner pattern

This commit is contained in:
2023-06-26 00:08:48 -04:00
parent bebd996796
commit 60921dcc76
3 changed files with 132 additions and 128 deletions
+22 -30
View File
@@ -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 = $('');
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() {
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 (
<div className={k.app}>
<Header />
<div className={k.picker}><label>Underlying</label><UnderlyingPicker /></div>
<div className={k.picker}><label>Quote Date</label><QuoteDatePicker /></div>
<div className={k.picker}><label>Strike</label><StrikePicker /></div>
<div className={k.picker}><label>Front Expiration</label><FrontMonthPicker /></div>
<div className={k.picker}><label>Back Expiration</label><BackMonthPicker /></div>
<div className={k.picker}><label>Underlying</label><Picker $url={$underlyingsUrl} $selectedOption={$selectedUnderlying} /></div>
<div className={k.picker}><label>Quote Date</label><Picker $url={$quoteDatePickerUrl} $isEnabled={$isQuoteDatePickerEnabled} $selectedOption={$selectedQuoteDate}/></div>
<div className={k.picker}><label>Strike</label><Picker $url={$strikePickerUrl} $isEnabled={$isStrikePickerEnabled} $selectedOption={$selectedStrike}/></div>
<div className={k.picker}><label>Front Expiration</label><Picker $url={$frontMonthExpirationPickerUrl} $isEnabled={$isFrontMonthExpirationPickerEnabled} /></div>
<div className={k.picker}><label>Back Expiration</label><Picker $url={$backMonthExpirationPickerUrl} $isEnabled={$isBackMonthExpirationPickerEnabled} /></div>
<HistoricalImpliedVolatilityChart />
</div>
);
+50 -40
View File
@@ -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<Array<string>>, $isLoading?:PrimitiveAtom<boolean>, $selectedOption?:PrimitiveAtom<string>, $url:Atom<string>, $isEnabled?:Atom<boolean> };
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<Array<string>>,
$isLoading?:PrimitiveAtom<boolean>,
$selectedOption?:PrimitiveAtom<string>,
$url:Atom<string>,
$isEnabled?:Atom<boolean>
};
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 (
<div>
{isLoading
?
<span>Loading...</span>
:
<select value={selectedOption} onChange={(e)=>{ setSelectedOption(e.target.value); }}>
<option key="" value=""></option>
{options.map((date)=>
<option key={date} value={date}>{date}</option>
)}
</select>
}
</div>
);
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 (
<Provider>
<div>
{isLoading
?
<span>Loading...</span>
:
<select value={selectedOption} onChange={(e)=>{ setSelectedOption(e.target.value); }}>
<option key="" value=""></option>
{options.map((date)=>
<option key={date} value={date}>{date}</option>
)}
</select>
}
</div>
</Provider>
);
}