react-soda
Version:
😉 Simpler and more effective state management for react.
124 lines (123 loc) • 4.49 kB
JavaScript
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 };