in packages/recoil-sync/RecoilSync.js [419:549]
throw err(
'Recoil does not yet support setting atoms to an asynchronous state',
);
}
// If this effect set the atom, don't bother with lower-priority
// effects. But, if the item didn't have a value then reset
// below but ontinue falling back on other effects for the same
// storage. This can happen if multiple effects are used to
// migrate to a new itemKey and we want to read from the
// older key as a fallback.
break;
} else {
atomRegistration.pendingUpdate = {value: DEFAULT_VALUE};
reset(atomRegistration.atom);
}
}
}
}
},
[recoilStoreID, storeKey, read],
);
const updateItem = useCallback(
<T>(itemKey: ItemKey, newValue: DefaultValue | T) => {
updateItems(new Map([[itemKey, newValue]]));
},
[updateItems],
);
const updateAllKnownItems = useCallback(
itemSnapshot => {
// Reset the value of any items that are registered and not included in
// the user-provided snapshot.
const atomRegistry = registries.getAtomRegistry(recoilStoreID, storeKey);
for (const [, registration] of atomRegistry) {
for (const [, {subscribedItemKeys}] of registration.effects) {
for (const itemKey of subscribedItemKeys) {
if (!itemSnapshot.has(itemKey)) {
itemSnapshot.set(itemKey, DEFAULT_VALUE);
}
}
}
}
updateItems(itemSnapshot);
},
[recoilStoreID, storeKey, updateItems],
);
useEffect(
() =>
// TODO try/catch errors and set atom to error state if actionOnFailure is errorState
listen?.({updateItem, updateAllKnownItems}),
[updateItem, updateAllKnownItems, listen],
);
// Register Storage
// Save before effects so that we can initialize atoms for initial render
registries.setStorage(recoilStoreID, storeKey, {write, read});
useEffect(
() => registries.setStorage(recoilStoreID, storeKey, {write, read}),
[recoilStoreID, storeKey, read, write],
);
}
function RecoilSync(props: RecoilSyncOptions): React.Node {
useRecoilSync(props);
return null;
}
///////////////////////
// syncEffect()
///////////////////////
export type ReadAtomInterface = {read: ReadItem};
export type ReadAtom = ReadAtomInterface =>
| DefaultValue
| Promise<DefaultValue | mixed>
| Loadable<DefaultValue | mixed>
| mixed;
export type WriteAtomInterface = {
write: WriteItem,
reset: ResetItem,
read: ReadItem,
};
export type WriteAtom<T> = (WriteAtomInterface, DefaultValue | T) => void;
export type SyncEffectOptions<T> = {
storeKey?: StoreKey,
itemKey?: ItemKey,
refine: Checker<T>,
read?: ReadAtom,
write?: WriteAtom<T>,
// Sync actual default value instead of empty when atom is in default state
syncDefault?: boolean,
// If there is a failure reading or refining the value, should the atom
// silently use the default value or be put in an error state
actionOnFailure_UNSTABLE?: ActionOnFailure,
};
function syncEffect<T>(opt: SyncEffectOptions<T>): AtomEffect<T> {
return ({node, trigger, storeID, setSelf, getLoadable, getInfo_UNSTABLE}) => {
// Get options with defaults
const itemKey = opt.itemKey ?? node.key;
const options: AtomSyncOptions<T> = {
itemKey,
read: ({read}) => read(itemKey),
write: ({write}, loadable) => write(itemKey, loadable),
syncDefault: false,
actionOnFailure_UNSTABLE: 'errorState',
...opt,
};
const {storeKey} = options;
const storage = registries.getStorage(storeID, storeKey);
// Register Atom
const {effectRegistration, unregisterEffect} = registries.setAtomEffect(
storeID,
storeKey,
node,
options,
);
if (trigger === 'get') {
// Initialize Atom value
const readFromStorage = storage?.read;
if (readFromStorage != null) {
try {
const loadable = readAtomItems(effectRegistration, readFromStorage);
if (loadable != null) {
switch (loadable.state) {