UNPKG

jotai

Version:

👻 Primitive and flexible state management for React

110 lines (109 loc) • 3.7 kB
import { atom } from '../../vanilla.js'; const isPromiseLike = (x) => typeof x?.then === 'function'; const getSymbolObservable = () => Symbol.observable; export function atomWithObservable(getObservable, options) { const returnResultData = (result) => { if ('e' in result) { throw result.e; } return result.d; }; const observableResultAtom = atom((get) => { const observable = getObservable(get); const symbolObservable = getSymbolObservable() ?? 'undefined'; const subscribable = observable[symbolObservable]?.() || observable; let resolve; const makePending = () => new Promise((r) => { resolve = r; }); const initialResult = options && 'initialValue' in options ? { d: typeof options.initialValue === 'function' ? options.initialValue() : options.initialValue, } : makePending(); let setResult; let lastResult; const listener = (result) => { lastResult = result; resolve?.(result); setResult?.(result); }; let subscription; let timer; const isNotMounted = () => !setResult; const unsubscribe = () => { if (subscription) { subscription.unsubscribe(); subscription = undefined; } }; const start = () => { if (subscription) { clearTimeout(timer); subscription.unsubscribe(); } subscription = subscribable.subscribe({ next: (d) => listener({ d }), error: (e) => listener({ e }), complete: () => { }, }); if (isNotMounted() && options?.unstable_timeout) { timer = setTimeout(unsubscribe, options.unstable_timeout); } }; start(); const resultAtom = atom(lastResult || initialResult); if (process.env.NODE_ENV !== 'production') { resultAtom.debugPrivate = true; } resultAtom.onMount = (update) => { setResult = update; if (lastResult) { update(lastResult); } if (subscription) { clearTimeout(timer); } else { start(); } return () => { setResult = undefined; if (options?.unstable_timeout) { timer = setTimeout(unsubscribe, options.unstable_timeout); } else { unsubscribe(); } }; }; return [resultAtom, observable, makePending, start, isNotMounted]; }); if (process.env.NODE_ENV !== 'production') { observableResultAtom.debugPrivate = true; } const observableAtom = atom((get) => { const [resultAtom] = get(observableResultAtom); const result = get(resultAtom); if (isPromiseLike(result)) { return result.then(returnResultData); } return returnResultData(result); }, (get, set, data) => { const [resultAtom, observable, makePending, start, isNotMounted] = get(observableResultAtom); if ('next' in observable) { if (isNotMounted()) { set(resultAtom, makePending()); start(); } observable.next(data); } else { throw new Error('observable is not subject'); } }); return observableAtom; }