in packages/recoil/hooks/Recoil_Hooks.js [487:567]
function useRecoilValueLoadable_LEGACY<T>(
recoilValue: RecoilValue<T>,
): Loadable<T> {
const storeRef = useStoreRef();
const [, forceUpdate] = useState([]);
const componentName = useComponentName();
const getLoadable = useCallback(() => {
if (__DEV__) {
recoilComponentGetRecoilValueCount_FOR_TESTING.current++;
}
const store = storeRef.current;
const storeState = store.getState();
const treeState = reactMode().early
? storeState.nextTree ?? storeState.currentTree
: storeState.currentTree;
return getRecoilValueAsLoadable(store, recoilValue, treeState);
}, [storeRef, recoilValue]);
const loadable = getLoadable();
const prevLoadableRef = useRef(loadable);
useEffect(() => {
prevLoadableRef.current = loadable;
});
useEffect(() => {
const store = storeRef.current;
const storeState = store.getState();
const subscription = subscribeToRecoilValue(
store,
recoilValue,
_state => {
if (!gkx('recoil_suppress_rerender_in_callback')) {
return forceUpdate([]);
}
const newLoadable = getLoadable();
if (!prevLoadableRef.current?.is(newLoadable)) {
forceUpdate(newLoadable);
}
prevLoadableRef.current = newLoadable;
},
componentName,
);
/**
* Since we're subscribing in an effect we need to update to the latest
* value of the atom since it may have changed since we rendered. We can
* go ahead and do that now, unless we're in the middle of a batch --
* in which case we should do it at the end of the batch, due to the
* following edge case: Suppose an atom is updated in another useEffect
* of this same component. Then the following sequence of events occur:
* 1. Atom is updated and subs fired (but we may not be subscribed
* yet depending on order of effects, so we miss this) Updated value
* is now in nextTree, but not currentTree.
* 2. This effect happens. We subscribe and update.
* 3. From the update we re-render and read currentTree, with old value.
* 4. Batcher's effect sets currentTree to nextTree.
* In this sequence we miss the update. To avoid that, add the update
* to queuedComponentCallback if a batch is in progress.
*/
if (storeState.nextTree) {
store.getState().queuedComponentCallbacks_DEPRECATED.push(() => {
prevLoadableRef.current = null;
forceUpdate([]);
});
} else {
if (!gkx('recoil_suppress_rerender_in_callback')) {
return forceUpdate([]);
}
const newLoadable = getLoadable();
if (!prevLoadableRef.current?.is(newLoadable)) {
forceUpdate(newLoadable);
}
prevLoadableRef.current = newLoadable;
}
return subscription.release;
}, [componentName, getLoadable, recoilValue, storeRef]);
return loadable;
}