UNPKG

jotai-valtio

Version:
79 lines (78 loc) • 2.59 kB
import { atom } from 'jotai/vanilla'; import { proxy, snapshot, subscribe } from 'valtio/vanilla'; import { deepClone } from 'valtio/vanilla/utils'; export function mutableAtom(initialValue, options = defaultOptions) { const valueAtom = atom({ value: initialValue }); if (process.env.NODE_ENV !== 'production') { valueAtom.debugPrivate = true; } const { proxyFn } = { ...defaultOptions, ...options }; const storeAtom = atom((_get, { setSelf }) => { const store = { proxyState: createProxyState(() => store), getValue: () => setSelf({ type: 'getValue' }), setValue: (value) => setSelf({ type: 'setValue', payload: value }), }; return store; }, (get, set, action) => { if (action.type === 'setValue') { set(valueAtom, { value: action.payload }); } else if (action.type === 'getValue') { return get(valueAtom).value; } }); if (process.env.NODE_ENV !== 'production') { storeAtom.debugPrivate = true; } /** * sync the proxy state with the atom */ function onChange(getStore) { return () => { const { proxyState, getValue, setValue } = getStore(); const { value } = snapshot(proxyState); if (!Object.is(value, getValue())) { setValue(value); } }; } /** * create the proxy state and subscribe to it */ function createProxyState(getStore) { const proxyState = proxyFn({ value: deepClone(initialValue) }); // We never unsubscribe, but it's garbage collectable. subscribe(proxyState, onChange(getStore), true); return proxyState; } /** * wrap the proxy state in a proxy to ensure rerender on value change */ function wrapProxyState(proxyState) { return new Proxy(proxyState, { get(target, property) { return target[property]; }, set(target, property, value) { if (property === 'value') { target[property] = value; return true; } return false; }, }); } /** * create an atom that returns the proxy state */ const proxyEffectAtom = atom((get) => { get(valueAtom); // subscribe to value updates const store = get(storeAtom); return wrapProxyState(store.proxyState); }); return proxyEffectAtom; } const defaultOptions = { proxyFn: proxy, };