addons/isl/src/third-party/jotai-devtools/utils/useAtomsDevtools.tsblame
View source
b69ab311import { useEffect, useRef } from 'react';
b69ab312import { AnyAtom, AnyAtomValue, AtomsSnapshot } from '../types';
b69ab313import {
b69ab314 Connection,
b69ab315 createReduxConnection,
b69ab316} from './redux-extension/createReduxConnection';
b69ab317import { getReduxExtension } from './redux-extension/getReduxExtension';
b69ab318import { SnapshotOptions, useAtomsSnapshot } from './useAtomsSnapshot';
b69ab319import { useGotoAtomsSnapshot } from './useGotoAtomsSnapshot';
b69ab3110
b69ab3111const atomToPrintable = (atom: AnyAtom) =>
b69ab3112 atom.debugLabel ? `${atom}:${atom.debugLabel}` : `${atom}`;
b69ab3113
b69ab3114const getDevtoolsState = (atomsSnapshot: AtomsSnapshot) => {
b69ab3115 const values: Record<string, AnyAtomValue> = {};
b69ab3116 atomsSnapshot.values.forEach((v, atom) => {
b69ab3117 values[atomToPrintable(atom)] = v;
b69ab3118 });
b69ab3119 const dependents: Record<string, string[]> = {};
b69ab3120 atomsSnapshot.dependents.forEach((d, atom) => {
b69ab3121 dependents[atomToPrintable(atom)] = Array.from(d).map(atomToPrintable);
b69ab3122 });
b69ab3123 return {
b69ab3124 values,
b69ab3125 dependents,
b69ab3126 };
b69ab3127};
b69ab3128
b69ab3129type DevtoolsOptions = SnapshotOptions & {
b69ab3130 enabled?: boolean;
b69ab3131};
b69ab3132
b69ab3133export function useAtomsDevtools(
b69ab3134 name: string,
b69ab3135 options?: DevtoolsOptions,
b69ab3136): void {
b69ab3137 const { enabled } = options || {};
b69ab3138
b69ab3139 const extension = getReduxExtension(enabled);
b69ab3140
b69ab3141 // This an exception, we don't usually use utils in themselves!
b69ab3142 const atomsSnapshot = useAtomsSnapshot(options);
b69ab3143 const goToSnapshot = useGotoAtomsSnapshot(options);
b69ab3144
b69ab3145 const isTimeTraveling = useRef(false);
b69ab3146 const isRecording = useRef(true);
b69ab3147 const devtools = useRef<Connection>();
b69ab3148
b69ab3149 const snapshots = useRef<AtomsSnapshot[]>([]);
b69ab3150
b69ab3151 useEffect(() => {
b69ab3152 if (!extension) {
b69ab3153 return;
b69ab3154 }
b69ab3155 const getSnapshotAt = (index = snapshots.current.length - 1) => {
b69ab3156 // index 0 is @@INIT, so we need to return the next action (0)
b69ab3157 const snapshot = snapshots.current[index >= 0 ? index : 0];
b69ab3158 if (!snapshot) {
b69ab3159 throw new Error('snapshot index out of bounds');
b69ab3160 }
b69ab3161 return snapshot;
b69ab3162 };
b69ab3163
b69ab3164 devtools.current = createReduxConnection(extension, name);
b69ab3165
b69ab3166 const devtoolsUnsubscribe = devtools.current?.subscribe((message) => {
b69ab3167 switch (message.type) {
b69ab3168 case 'DISPATCH':
b69ab3169 switch (message.payload?.type) {
b69ab3170 case 'RESET':
b69ab3171 // TODO
b69ab3172 break;
b69ab3173
b69ab3174 case 'COMMIT':
b69ab3175 devtools.current?.init(getDevtoolsState(getSnapshotAt()));
b69ab3176 snapshots.current = [];
b69ab3177 break;
b69ab3178
b69ab3179 case 'JUMP_TO_ACTION':
b69ab3180 case 'JUMP_TO_STATE':
b69ab3181 isTimeTraveling.current = true;
b69ab3182 goToSnapshot(getSnapshotAt(message.payload.actionId - 1));
b69ab3183 break;
b69ab3184
b69ab3185 case 'PAUSE_RECORDING':
b69ab3186 isRecording.current = !isRecording.current;
b69ab3187 break;
b69ab3188 }
b69ab3189 }
b69ab3190 });
b69ab3191
b69ab3192 return () => {
b69ab3193 extension?.disconnect?.();
b69ab3194 devtoolsUnsubscribe?.();
b69ab3195 };
b69ab3196 }, [extension, goToSnapshot, name]);
b69ab3197
b69ab3198 useEffect(() => {
b69ab3199 if (!devtools.current) {
b69ab31100 return;
b69ab31101 }
b69ab31102 if (devtools.current.shouldInit) {
b69ab31103 devtools.current.init(undefined);
b69ab31104 devtools.current.shouldInit = false;
b69ab31105 return;
b69ab31106 }
b69ab31107 if (isTimeTraveling.current) {
b69ab31108 isTimeTraveling.current = false;
b69ab31109 } else if (isRecording.current) {
b69ab31110 snapshots.current.push(atomsSnapshot);
b69ab31111 devtools.current.send(
b69ab31112 {
b69ab31113 type: `${snapshots.current.length}`,
b69ab31114 updatedAt: new Date().toLocaleString(),
b69ab31115 } as any,
b69ab31116 getDevtoolsState(atomsSnapshot),
b69ab31117 );
b69ab31118 }
b69ab31119 }, [atomsSnapshot]);
b69ab31120}