UNPKG

jotai

Version:

👻 Primitive and flexible state management for React

172 lines (166 loc) • 4.83 kB
'use client'; import React, { createContext, useContext, useRef, createElement, useReducer, useEffect, useDebugValue, useCallback } from 'react'; import { getDefaultStore, createStore } from 'jotai/vanilla'; import { INTERNAL_registerAbortHandler } from 'jotai/vanilla/internals'; const StoreContext = createContext( void 0 ); function useStore(options) { const store = useContext(StoreContext); return (options == null ? void 0 : options.store) || store || getDefaultStore(); } function Provider({ children, store }) { const storeRef = useRef(void 0); if (!store && !storeRef.current) { storeRef.current = createStore(); } return createElement( StoreContext.Provider, { value: store || storeRef.current }, children ); } const isPromiseLike = (x) => typeof (x == null ? void 0 : x.then) === "function"; const attachPromiseStatus = (promise) => { if (!promise.status) { promise.status = "pending"; promise.then( (v) => { promise.status = "fulfilled"; promise.value = v; }, (e) => { promise.status = "rejected"; promise.reason = e; } ); } }; const use = React.use || // A shim for older React versions ((promise) => { if (promise.status === "pending") { throw promise; } else if (promise.status === "fulfilled") { return promise.value; } else if (promise.status === "rejected") { throw promise.reason; } else { attachPromiseStatus(promise); throw promise; } }); const continuablePromiseMap = /* @__PURE__ */ new WeakMap(); const createContinuablePromise = (promise, getValue) => { let continuablePromise = continuablePromiseMap.get(promise); if (!continuablePromise) { continuablePromise = new Promise((resolve, reject) => { let curr = promise; const onFulfilled = (me) => (v) => { if (curr === me) { resolve(v); } }; const onRejected = (me) => (e) => { if (curr === me) { reject(e); } }; const onAbort = () => { try { const nextValue = getValue(); if (isPromiseLike(nextValue)) { continuablePromiseMap.set(nextValue, continuablePromise); curr = nextValue; nextValue.then(onFulfilled(nextValue), onRejected(nextValue)); INTERNAL_registerAbortHandler(nextValue, onAbort); } else { resolve(nextValue); } } catch (e) { reject(e); } }; promise.then(onFulfilled(promise), onRejected(promise)); INTERNAL_registerAbortHandler(promise, onAbort); }); continuablePromiseMap.set(promise, continuablePromise); } return continuablePromise; }; function useAtomValue(atom, options) { const { delay, unstable_promiseStatus: promiseStatus = !React.use } = options || {}; const store = useStore(options); const [[valueFromReducer, storeFromReducer, atomFromReducer], rerender] = useReducer( (prev) => { const nextValue = store.get(atom); if (Object.is(prev[0], nextValue) && prev[1] === store && prev[2] === atom) { return prev; } return [nextValue, store, atom]; }, void 0, () => [store.get(atom), store, atom] ); let value = valueFromReducer; if (storeFromReducer !== store || atomFromReducer !== atom) { rerender(); value = store.get(atom); } useEffect(() => { const unsub = store.sub(atom, () => { if (promiseStatus) { try { const value2 = store.get(atom); if (isPromiseLike(value2)) { attachPromiseStatus( createContinuablePromise(value2, () => store.get(atom)) ); } } catch (e) { } } if (typeof delay === "number") { setTimeout(rerender, delay); return; } rerender(); }); rerender(); return unsub; }, [store, atom, delay, promiseStatus]); useDebugValue(value); if (isPromiseLike(value)) { const promise = createContinuablePromise(value, () => store.get(atom)); if (promiseStatus) { attachPromiseStatus(promise); } return use(promise); } return value; } function useSetAtom(atom, options) { const store = useStore(options); const setAtom = useCallback( (...args) => { if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && !("write" in atom)) { throw new Error("not writable atom"); } return store.set(atom, ...args); }, [store, atom] ); return setAtom; } function useAtom(atom, options) { return [ useAtomValue(atom, options), // We do wrong type assertion here, which results in throwing an error. useSetAtom(atom, options) ]; } export { Provider, useAtom, useAtomValue, useSetAtom, useStore };