UNPKG

jotai

Version:

👻 Primitive and flexible state management for React

88 lines (87 loc) • 3.3 kB
import { atom } from '../../vanilla.js'; const getCached = (c, m, k) => (m.has(k) ? m : m.set(k, c())).get(k); const cache1 = new WeakMap(); const memo2 = (create, dep1, dep2) => { const cache2 = getCached(() => new WeakMap(), cache1, dep1); return getCached(create, cache2, dep2); }; const isPromiseLike = (p) => typeof p?.then === 'function'; const defaultFallback = () => undefined; export function unwrap(anAtom, fallback = defaultFallback) { return memo2(() => { const promiseErrorCache = new WeakMap(); const promiseResultCache = new WeakMap(); const refreshAtom = atom(0); const triggerRefreshAtom = atom([]); triggerRefreshAtom.INTERNAL_onInit = (store) => { store.set(triggerRefreshAtom, [ () => store.set(refreshAtom, (c) => c + 1), ]); }; if (process.env.NODE_ENV !== 'production') { refreshAtom.debugPrivate = true; triggerRefreshAtom.debugPrivate = true; } const promiseAndValueAtom = atom((get) => { get(refreshAtom); let prev; try { prev = get(promiseAndValueAtom); } catch { // ignore previous errors to avoid getting stuck in error state } const promise = get(anAtom); if (!isPromiseLike(promise)) { return { v: promise }; } if (promise !== prev?.p) { promise.then((v) => { promiseResultCache.set(promise, v); const [triggerRefresh] = get(triggerRefreshAtom); triggerRefresh(); }, (e) => { promiseErrorCache.set(promise, e); const [triggerRefresh] = get(triggerRefreshAtom); triggerRefresh(); }); } if (promiseErrorCache.has(promise)) { if (prev && 'e' in prev && prev.p === promise) { return prev; } const e = promiseErrorCache.get(promise); if (prev && 'v' in prev) { return { p: promise, e, v: prev.v }; } return { p: promise, e }; } if (promiseResultCache.has(promise)) { return { p: promise, v: promiseResultCache.get(promise), }; } if (prev && 'v' in prev) { return { p: promise, f: fallback(prev.v), v: prev.v }; } return { p: promise, f: fallback() }; }); // HACK to read PromiseAndValue atom before initialization promiseAndValueAtom.init = undefined; if (process.env.NODE_ENV !== 'production') { promiseAndValueAtom.debugPrivate = true; } return atom((get) => { const state = get(promiseAndValueAtom); if ('e' in state) { throw state.e; } if ('f' in state) { // is pending return state.f; } return state.v; }, (_get, set, ...args) => set(anAtom, ...args)); }, anAtom, fallback); }