in packages/recoil-sync/RecoilSync_URL.js [159:255]
function useRecoilURLSync({
storeKey,
location: loc,
serialize,
deserialize,
browserInterface,
}: RecoilURLSyncOptions): void {
const {getURL, replaceURL, pushURL, listenChangeURL} = {
...DEFAULT_BROWSER_INTERFACE,
...(browserInterface ?? {}),
};
// Parse and cache the current state from the URL
// Update cached URL parsing if properties of location prop change, but not
// based on just the object reference itself.
const memoizedLoc = useMemo(
() => loc,
// Complications with disjoint uniont
// $FlowIssue[prop-missing]
[loc.part, loc.queryParam], // eslint-disable-line fb-www/react-hooks-deps
);
const updateCachedState: () => void = useCallback(() => {
cachedState.current = parseURL(getURL(), memoizedLoc, deserialize);
}, [getURL, memoizedLoc, deserialize]);
const cachedState = useRef<?ItemSnapshot>(null);
// Avoid executing updateCachedState() on each render
const firstRender = useRef(true);
firstRender.current && updateCachedState();
firstRender.current = false;
useEffect(updateCachedState, [updateCachedState]);
const write = useCallback(
({diff, allItems}) => {
updateCachedState(); // Just to be safe...
// This could be optimized with an itemKey-based registery if necessary to avoid
// atom traversal.
const atomRegistry = registries.get(storeKey);
const itemsToPush =
atomRegistry != null
? new Set(
Array.from(atomRegistry)
.filter(
([, {history, itemKeys}]) =>
history === 'push' &&
Array.from(itemKeys).some(key => diff.has(key)),
)
.map(([, {itemKeys}]) => itemKeys)
.reduce(
(itemKeys, keys) => itemKeys.concat(Array.from(keys)),
[],
),
)
: null;
if (itemsToPush?.size && cachedState.current != null) {
const replaceItems: ItemSnapshot = cachedState.current;
// First, repalce the URL with any atoms that replace the URL history
for (const [key, value] of allItems) {
if (!itemsToPush.has(key)) {
replaceItems.set(key, value);
}
}
replaceURL(encodeURL(getURL(), loc, replaceItems, serialize));
// Next, push the URL with any atoms that caused a new URL history entry
pushURL(encodeURL(getURL(), loc, allItems, serialize));
} else {
// Just replace the URL with the new state
replaceURL(encodeURL(getURL(), loc, allItems, serialize));
}
cachedState.current = allItems;
},
[getURL, loc, pushURL, replaceURL, serialize, storeKey, updateCachedState],
);
const read: ReadItem = useCallback(itemKey => {
return cachedState.current?.has(itemKey)
? cachedState.current?.get(itemKey)
: new DefaultValue();
}, []);
const listen = useCallback(
({updateAllKnownItems}) => {
function handleUpdate() {
updateCachedState();
if (cachedState.current != null) {
updateAllKnownItems(cachedState.current);
}
}
return listenChangeURL(handleUpdate);
},
[listenChangeURL, updateCachedState],
);
useRecoilSync({storeKey, read, write, listen});
}