I am trying to figure out how to solve the following problem in the best way possible:
I have multiple components all requiring a global state (I am using recoil for this, since I have many different "atom" states).
Only if a component gets loaded that requires that state, it will perform an expensive computation that fetches the data. This should happen only once upon initialisation. Other components that require the same piece of data should not re-trigger the data fetching, unless they explicitly call an updateState
function.
Ideally, my implementation would look something like this:
const initialState = {
uri: '',
balance: '0',
};
const fetchExpensiveState = () => {
uri: await fetchURI(),
balance: await fetchBalance(),
});
const MyExpensiveData = atom({
key: 'expensiveData',
default: initialState,
updateState: fetchExpensiveState,
});
function Component1() {
const data = useRecoilMemo(MyExpensiveData); // triggers `fetchExpensiveState` upon first call
return ...
}
function Component2() {
const data = useRecoilMemo(MyExpensiveData); // doesn't trigger `fetchExpensiveState` a second time
return ...
}
I could solve this by using useRecoilState
and additional variables in the context that tell me whether this has been initialised already, like so:
export function useExpensiveState() {
const [context, setContext] = useRecoilState(MyExpensiveData);
const updateState = useCallback(async () => {
setContext({...fetchExpensiveState(), loaded: true});
}, []);
useEffect(() => {
if (!context.loaded) {
setContext({...context, loaded: true});
updateState();
}
}, []);
return { ...context, updateState };
}
It would be possible to make this solution more elegant (not mixing loaded
with the state for example). Although, because this should be imo essential and basic, it seems as though I'm missing some solution that I haven't come across yet.