| b69ab31 | | | 1 | import { isDev as __DEV__ } from '../../../utils'; |
| b69ab31 | | | 2 | |
| b69ab31 | | | 3 | import { useStore } from 'jotai/react'; |
| b69ab31 | | | 4 | import type { Atom } from 'jotai/vanilla'; |
| b69ab31 | | | 5 | import { |
| b69ab31 | | | 6 | useDebugValue, |
| b69ab31 | | | 7 | useEffect, |
| b69ab31 | | | 8 | useLayoutEffect, |
| b69ab31 | | | 9 | useRef, |
| b69ab31 | | | 10 | useState, |
| b69ab31 | | | 11 | } from 'react'; |
| b69ab31 | | | 12 | |
| b69ab31 | | | 13 | type Store = ReturnType<typeof useStore>; |
| b69ab31 | | | 14 | type AtomState = NonNullable< |
| b69ab31 | | | 15 | ReturnType<NonNullable<Store['dev_get_atom_state']>> |
| b69ab31 | | | 16 | >; |
| b69ab31 | | | 17 | |
| b69ab31 | | | 18 | const atomToPrintable = (atom: Atom<unknown>) => |
| b69ab31 | | | 19 | atom.debugLabel || atom.toString(); |
| b69ab31 | | | 20 | |
| b69ab31 | | | 21 | const stateToPrintable = ([store, atoms]: [Store, Atom<unknown>[]]) => |
| b69ab31 | | | 22 | Object.fromEntries( |
| b69ab31 | | | 23 | atoms.flatMap((atom) => { |
| b69ab31 | | | 24 | const mounted = store.dev_get_mounted?.(atom); |
| b69ab31 | | | 25 | if (!mounted) { |
| b69ab31 | | | 26 | return []; |
| b69ab31 | | | 27 | } |
| b69ab31 | | | 28 | const dependents = mounted.t; |
| b69ab31 | | | 29 | const atomState = store.dev_get_atom_state?.(atom) || ({} as AtomState); |
| b69ab31 | | | 30 | return [ |
| b69ab31 | | | 31 | [ |
| b69ab31 | | | 32 | atomToPrintable(atom), |
| b69ab31 | | | 33 | { |
| b69ab31 | | | 34 | ...('e' in atomState && { error: atomState.e }), |
| b69ab31 | | | 35 | ...('v' in atomState && { value: atomState.v }), |
| b69ab31 | | | 36 | dependents: Array.from(dependents).map(atomToPrintable), |
| b69ab31 | | | 37 | }, |
| b69ab31 | | | 38 | ], |
| b69ab31 | | | 39 | ]; |
| b69ab31 | | | 40 | }), |
| b69ab31 | | | 41 | ); |
| b69ab31 | | | 42 | |
| b69ab31 | | | 43 | type Options = Parameters<typeof useStore>[0] & { |
| b69ab31 | | | 44 | enabled?: boolean; |
| b69ab31 | | | 45 | }; |
| b69ab31 | | | 46 | |
| b69ab31 | | | 47 | // We keep a reference to the atoms, |
| b69ab31 | | | 48 | // so atoms aren't garbage collected by the WeakMap of mounted atoms |
| b69ab31 | | | 49 | export const useAtomsDebugValue = (options?: Options) => { |
| b69ab31 | | | 50 | const enabled = options?.enabled ?? __DEV__; |
| b69ab31 | | | 51 | const store = useStore(options); |
| b69ab31 | | | 52 | const [atoms, setAtoms] = useState<Atom<unknown>[]>([]); |
| b69ab31 | | | 53 | const duringReactRenderPhase = useRef(true); |
| b69ab31 | | | 54 | duringReactRenderPhase.current = true; |
| b69ab31 | | | 55 | useLayoutEffect(() => { |
| b69ab31 | | | 56 | duringReactRenderPhase.current = false; |
| b69ab31 | | | 57 | }); |
| b69ab31 | | | 58 | useEffect(() => { |
| b69ab31 | | | 59 | const devSubscribeStore: Store['dev_subscribe_store'] = |
| b69ab31 | | | 60 | // @ts-expect-error dev_subscribe_state is deprecated in <= 2.0.3 |
| b69ab31 | | | 61 | store?.dev_subscribe_store || store?.dev_subscribe_state; |
| b69ab31 | | | 62 | |
| b69ab31 | | | 63 | if (!enabled || !devSubscribeStore) { |
| b69ab31 | | | 64 | return; |
| b69ab31 | | | 65 | } |
| b69ab31 | | | 66 | const callback = () => { |
| b69ab31 | | | 67 | const deferrableAtomSetAction = () => |
| b69ab31 | | | 68 | setAtoms(Array.from(store.dev_get_mounted_atoms?.() || [])); |
| b69ab31 | | | 69 | if (duringReactRenderPhase.current) { |
| b69ab31 | | | 70 | // avoid set action when react is rendering components |
| b69ab31 | | | 71 | Promise.resolve().then(deferrableAtomSetAction); |
| b69ab31 | | | 72 | } else { |
| b69ab31 | | | 73 | deferrableAtomSetAction(); |
| b69ab31 | | | 74 | } |
| b69ab31 | | | 75 | }; |
| b69ab31 | | | 76 | // FIXME replace this with `store.dev_subscribe_store` check after next minor Jotai 2.1.0? |
| b69ab31 | | | 77 | if (!('dev_subscribe_store' in store)) { |
| b69ab31 | | | 78 | console.warn( |
| b69ab31 | | | 79 | "[DEPRECATION-WARNING] Jotai version you're using contains deprecated dev-only properties that will be removed soon. Please update to the latest version of Jotai.", |
| b69ab31 | | | 80 | ); |
| b69ab31 | | | 81 | } |
| b69ab31 | | | 82 | |
| b69ab31 | | | 83 | const unsubscribe = devSubscribeStore?.(callback, 2); |
| b69ab31 | | | 84 | callback(); |
| b69ab31 | | | 85 | return unsubscribe; |
| b69ab31 | | | 86 | }, [enabled, store]); |
| b69ab31 | | | 87 | useDebugValue([store, atoms], stateToPrintable); |
| b69ab31 | | | 88 | }; |