addons/isl/src/third-party/jotai-devtools/utils/useAtomsDebugValue.tsblame
View source
b69ab311import { isDev as __DEV__ } from '../../../utils';
b69ab312
b69ab313import { useStore } from 'jotai/react';
b69ab314import type { Atom } from 'jotai/vanilla';
b69ab315import {
b69ab316 useDebugValue,
b69ab317 useEffect,
b69ab318 useLayoutEffect,
b69ab319 useRef,
b69ab3110 useState,
b69ab3111} from 'react';
b69ab3112
b69ab3113type Store = ReturnType<typeof useStore>;
b69ab3114type AtomState = NonNullable<
b69ab3115 ReturnType<NonNullable<Store['dev_get_atom_state']>>
b69ab3116>;
b69ab3117
b69ab3118const atomToPrintable = (atom: Atom<unknown>) =>
b69ab3119 atom.debugLabel || atom.toString();
b69ab3120
b69ab3121const stateToPrintable = ([store, atoms]: [Store, Atom<unknown>[]]) =>
b69ab3122 Object.fromEntries(
b69ab3123 atoms.flatMap((atom) => {
b69ab3124 const mounted = store.dev_get_mounted?.(atom);
b69ab3125 if (!mounted) {
b69ab3126 return [];
b69ab3127 }
b69ab3128 const dependents = mounted.t;
b69ab3129 const atomState = store.dev_get_atom_state?.(atom) || ({} as AtomState);
b69ab3130 return [
b69ab3131 [
b69ab3132 atomToPrintable(atom),
b69ab3133 {
b69ab3134 ...('e' in atomState && { error: atomState.e }),
b69ab3135 ...('v' in atomState && { value: atomState.v }),
b69ab3136 dependents: Array.from(dependents).map(atomToPrintable),
b69ab3137 },
b69ab3138 ],
b69ab3139 ];
b69ab3140 }),
b69ab3141 );
b69ab3142
b69ab3143type Options = Parameters<typeof useStore>[0] & {
b69ab3144 enabled?: boolean;
b69ab3145};
b69ab3146
b69ab3147// We keep a reference to the atoms,
b69ab3148// so atoms aren't garbage collected by the WeakMap of mounted atoms
b69ab3149export const useAtomsDebugValue = (options?: Options) => {
b69ab3150 const enabled = options?.enabled ?? __DEV__;
b69ab3151 const store = useStore(options);
b69ab3152 const [atoms, setAtoms] = useState<Atom<unknown>[]>([]);
b69ab3153 const duringReactRenderPhase = useRef(true);
b69ab3154 duringReactRenderPhase.current = true;
b69ab3155 useLayoutEffect(() => {
b69ab3156 duringReactRenderPhase.current = false;
b69ab3157 });
b69ab3158 useEffect(() => {
b69ab3159 const devSubscribeStore: Store['dev_subscribe_store'] =
b69ab3160 // @ts-expect-error dev_subscribe_state is deprecated in <= 2.0.3
b69ab3161 store?.dev_subscribe_store || store?.dev_subscribe_state;
b69ab3162
b69ab3163 if (!enabled || !devSubscribeStore) {
b69ab3164 return;
b69ab3165 }
b69ab3166 const callback = () => {
b69ab3167 const deferrableAtomSetAction = () =>
b69ab3168 setAtoms(Array.from(store.dev_get_mounted_atoms?.() || []));
b69ab3169 if (duringReactRenderPhase.current) {
b69ab3170 // avoid set action when react is rendering components
b69ab3171 Promise.resolve().then(deferrableAtomSetAction);
b69ab3172 } else {
b69ab3173 deferrableAtomSetAction();
b69ab3174 }
b69ab3175 };
b69ab3176 // FIXME replace this with `store.dev_subscribe_store` check after next minor Jotai 2.1.0?
b69ab3177 if (!('dev_subscribe_store' in store)) {
b69ab3178 console.warn(
b69ab3179 "[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.",
b69ab3180 );
b69ab3181 }
b69ab3182
b69ab3183 const unsubscribe = devSubscribeStore?.(callback, 2);
b69ab3184 callback();
b69ab3185 return unsubscribe;
b69ab3186 }, [enabled, store]);
b69ab3187 useDebugValue([store, atoms], stateToPrintable);
b69ab3188};