UNPKG

react-soda

Version:

😉 Simpler and more effective state management for react.

124 lines (123 loc) • 4.49 kB
import { useSyncExternalStore } from "react"; function isPlainObject(obj) { return "object" == typeof obj && null !== obj && Object.getPrototypeOf(obj) === Object.prototype; } function createStore(init, getServerSnapshot) { let nowState = "function" == typeof init ? init() : init; const listeners = new Set(); function getState(selector) { if ("function" == typeof selector) return selector(nowState); return nowState; } function setState(newState, replace) { if (Object.is(nowState, newState)) return; const prevState = nowState; const nextState = "function" == typeof newState ? newState(prevState) : newState; if (Object.is(prevState, nextState)) return; nowState = isPlainObject(prevState) && isPlainObject(nextState) && !replace ? Object.assign({}, prevState, nextState) : nextState; listeners.forEach((listener)=>listener(nowState, prevState)); } function subscribe(listener) { listeners.add(listener); return function() { listeners.delete(listener); }; } const _getState = getState; function useStore(selector) { function getState() { return _getState(selector); } const state = useSyncExternalStore(subscribe, getState, getServerSnapshot ? ()=>"function" == typeof selector ? selector(getServerSnapshot()) : getServerSnapshot() : void 0); return [ state, setState ]; } useStore.getState = getState; useStore.setState = setState; useStore.subscribe = subscribe; return useStore; } function createPersistentStore(init, optionOrString, getServerSnapshot) { const options = "string" == typeof optionOrString ? { name: optionOrString } : optionOrString; const { name, stringify = JSON.stringify, parse = JSON.parse } = options; const storage = "function" == typeof options.storage ? options.storage() : options.storage || globalThis.localStorage; const storageKey = `react-soda-${name}`; function getStorage() { return storage; } function getName() { return name; } function getStorageKey() { return storageKey; } function getStringify() { return stringify; } function getParse() { return parse; } function removeStorage() { storage.removeItem(storageKey); } const strOrPromise = storage.getItem(storageKey); let changed = false; if (strOrPromise instanceof Promise) strOrPromise.then((str)=>{ if (changed || null === str) return; let data; let success = false; try { data = parse(str); success = true; } catch (error) { storage.removeItem(storageKey); console.error(error); } if (success) useStore.setState(data); }).catch((error)=>{ console.error(error); }); else if (null !== strOrPromise) { let data; let success = false; try { data = parse(strOrPromise); success = true; } catch (error) { storage.removeItem(storageKey); console.error(error); } if (success) { const useStore = createStore(data, getServerSnapshot); useStore.getStorage = getStorage; useStore.getName = getName; useStore.getStorageKey = getStorageKey; useStore.getStringify = getStringify; useStore.getParse = getParse; useStore.removeStorage = removeStorage; storage.setItem(storageKey, stringify(useStore.getState())); useStore.subscribe((state)=>storage.setItem(storageKey, stringify(state))); return useStore; } } const useStore = createStore(init, getServerSnapshot); useStore.getStorage = getStorage; useStore.getName = getName; useStore.getStorageKey = getStorageKey; useStore.getStringify = getStringify; useStore.getParse = getParse; useStore.removeStorage = removeStorage; storage.setItem(storageKey, stringify(useStore.getState())); const unsubscribe = useStore.subscribe(()=>{ changed = true; unsubscribe(); }); useStore.subscribe((state)=>storage.setItem(storageKey, stringify(state))); return useStore; } const src = createStore; export { createPersistentStore, createStore, src as default, isPlainObject };