UNPKG

jotai-valtio

Version:
68 lines (62 loc) • 1.87 kB
import { atom } from 'jotai/vanilla'; import type { SetStateAction } from 'jotai/vanilla'; import { snapshot, subscribe } from 'valtio/vanilla'; const isObject = (x: unknown): x is object => typeof x === 'object' && x !== null && !(x instanceof Promise); const applyChanges = <T extends object>(proxyObject: T, prev: T, next: T) => { (Object.getOwnPropertyNames(prev) as (keyof T)[]).forEach((key) => { if (!(key in next)) { delete proxyObject[key]; } else if (Object.is(prev[key], next[key])) { // unchanged } else if ( isObject(proxyObject[key]) && isObject(prev[key]) && isObject(next[key]) ) { applyChanges( proxyObject[key] as unknown as object, prev[key] as unknown as object, next[key] as unknown as object, ); } else { proxyObject[key] = next[key]; } }); (Object.keys(next) as (keyof T)[]).forEach((key) => { if (!(key in prev)) { proxyObject[key] = next[key]; } }); }; type Options = { sync?: boolean; }; export function atomWithProxy<Value extends object>( proxyObject: Value, options?: Options, ) { const baseAtom = atom(snapshot(proxyObject)); if (process.env.NODE_ENV !== 'production') { baseAtom.debugPrivate = true; } baseAtom.onMount = (setValue) => { const callback = () => { setValue(snapshot(proxyObject)); }; const unsub = subscribe(proxyObject, callback, options?.sync); callback(); return unsub; }; const derivedAtom = atom( (get) => get(baseAtom) as Value, (get, _set, update: SetStateAction<Value>) => { const newValue = typeof update === 'function' ? (update as (prev: Value) => Value)(get(baseAtom) as Value) : update; applyChanges(proxyObject, snapshot(proxyObject) as Value, newValue); }, ); return derivedAtom; }