in packages/recoil/core/Recoil_RecoilRoot.js [353:527]
function RecoilRoot_INTERNAL({
initializeState_DEPRECATED,
initializeState,
store_INTERNAL: storeProp, // For use with React "context bridging"
children,
}: InternalProps): React.Node {
// prettier-ignore
// @fb-only: useEffect(() => {
// @fb-only: if (gkx('recoil_usage_logging')) {
// @fb-only: try {
// @fb-only: RecoilUsageLogFalcoEvent.log(() => ({
// @fb-only: type: RecoilusagelogEvent.RECOIL_ROOT_MOUNTED,
// @fb-only: path: URI.getRequestURI().getPath(),
// @fb-only: }));
// @fb-only: } catch {
// @fb-only: recoverableViolation(
// @fb-only: 'Error when logging Recoil Usage event',
// @fb-only: 'recoil',
// @fb-only: );
// @fb-only: }
// @fb-only: }
// @fb-only: }, []);
let storeStateRef: {current: StoreState}; // eslint-disable-line prefer-const
const getGraph = version => {
const graphs = storeStateRef.current.graphsByVersion;
if (graphs.has(version)) {
return nullthrows(graphs.get(version));
}
const newGraph = graph();
graphs.set(version, newGraph);
return newGraph;
};
const subscribeToTransactions = (callback, key) => {
if (key == null) {
// Global transaction subscriptions
const {transactionSubscriptions} = storeRef.current.getState();
const id = nextID++;
transactionSubscriptions.set(id, callback);
return {
release: () => {
transactionSubscriptions.delete(id);
},
};
} else {
// Node-specific transaction subscriptions:
const {nodeTransactionSubscriptions} = storeRef.current.getState();
if (!nodeTransactionSubscriptions.has(key)) {
nodeTransactionSubscriptions.set(key, new Map());
}
const id = nextID++;
nullthrows(nodeTransactionSubscriptions.get(key)).set(id, callback);
return {
release: () => {
const subs = nodeTransactionSubscriptions.get(key);
if (subs) {
subs.delete(id);
if (subs.size === 0) {
nodeTransactionSubscriptions.delete(key);
}
}
},
};
}
};
const addTransactionMetadata = (metadata: {...}) => {
startNextTreeIfNeeded(storeRef.current);
for (const k of Object.keys(metadata)) {
nullthrows(storeRef.current.getState().nextTree).transactionMetadata[k] =
metadata[k];
}
};
const replaceState = replacer => {
startNextTreeIfNeeded(storeRef.current);
// Use replacer to get the next state:
const nextTree = nullthrows(storeStateRef.current.nextTree);
let replaced;
try {
stateReplacerIsBeingExecuted = true;
replaced = replacer(nextTree);
} finally {
stateReplacerIsBeingExecuted = false;
}
if (replaced === nextTree) {
return;
}
if (__DEV__) {
if (typeof window !== 'undefined') {
window.$recoilDebugStates.push(replaced); // TODO this shouldn't happen here because it's not batched
}
}
// Save changes to nextTree and schedule a React update:
storeStateRef.current.nextTree = replaced;
if (reactMode().early) {
notifyComponents(storeRef.current, storeStateRef.current, replaced);
}
nullthrows(notifyBatcherOfChange.current)();
};
const notifyBatcherOfChange = useRef<null | (mixed => void)>(null);
const setNotifyBatcherOfChange = useCallback(
(x: mixed => void) => {
notifyBatcherOfChange.current = x;
},
[notifyBatcherOfChange],
);
const storeRef = useRefInitOnce(
() =>
storeProp ?? {
storeID: getNextStoreID(),
getState: () => storeStateRef.current,
replaceState,
getGraph,
subscribeToTransactions,
addTransactionMetadata,
},
);
if (storeProp != null) {
storeRef.current = storeProp;
}
storeStateRef = useRefInitOnce(() =>
initializeState_DEPRECATED != null
? initialStoreState_DEPRECATED(
storeRef.current,
initializeState_DEPRECATED,
)
: initializeState != null
? initialStoreState(initializeState)
: makeEmptyStoreState(),
);
const mutableSource = useMemo(
() =>
createMutableSource?.(
storeStateRef,
() => storeStateRef.current.currentTree.version,
),
[storeStateRef],
);
// Cleanup when the <RecoilRoot> is unmounted
useEffect(() => {
// React is free to call effect cleanup handlers and effects at will, the
// deps array is only an optimization. For example, React strict mode
// will execute each effect twice for testing. Therefore, we need symmetry
// to re-initialize all known atoms after they were cleaned up.
const store = storeRef.current;
for (const atomKey of new Set(store.getState().knownAtoms)) {
initializeNode(store, atomKey, 'get');
}
return () => {
for (const atomKey of store.getState().knownAtoms) {
cleanUpNode(store, atomKey);
}
};
}, [storeRef]);
return (
<AppContext.Provider value={storeRef}>
<MutableSourceContext.Provider value={mutableSource}>
<Batcher setNotifyBatcherOfChange={setNotifyBatcherOfChange} />
{children}
</MutableSourceContext.Provider>
</AppContext.Provider>
);
}