import { batch, createEffect, createSignal, onMount } from "solid-js"; import { createStore, reconcile } from "solid-js/store"; export const createLocalStore = function >( initState: T, { prefix = "app", serializer = (v: any) => JSON.stringify(v), deserializer = (v: any) => JSON.parse(v), } = {} ) { const [state, setState] = createStore(initState); const [mounted, setMounted] = createSignal(false); const localStorage = globalThis.localStorage; if (localStorage) { let mounts = 0; let mounted_ = false; const updating = {} as Record; const keys = Object.keys(state); const changedBeforeMount = {} as Record; for (const key of keys) { let storeKey = `${prefix}-${key}`; let mountValue = localStorage.getItem(storeKey); let initRun = true; const [updatingCount, setUpdatingCount] = createSignal(0); // TODO: Implement localStorage listener createEffect(() => { // During mounts we want to always run this effect even if the state value hasnt changed // We need to run it always to reset updating[key] updatingCount(); const isInitRun = initRun; initRun = false; const value = serializer(state[key]); if (isInitRun && mountValue) { return; } // If the key is getting mounted at the moment, we skip the localStorage set if (updating[key]) { updating[key] = false; return; } if (!mounted_) { changedBeforeMount[key] = true; } if (value === undefined) { localStorage.removeItem(storeKey); } else { localStorage.setItem(storeKey, value); } }); onMount(() => { if (!changedBeforeMount[key] && mountValue) { updating[key] = true; batch(function () { setUpdatingCount(updatingCount() + 1); setState(key as any, reconcile(deserializer(mountValue))); }); } mounts++; mountValue = null; if (mounts === keys.length) { setMounted(true); mounted_ = true; } }); } } return [state, setState, mounted] as [ typeof state, typeof setState, typeof mounted ]; };