From b23337fd593af00c0d772c4ef9fbae361f2cd353 Mon Sep 17 00:00:00 2001 From: Katja Lutz Date: Wed, 22 Jun 2022 21:33:00 +0200 Subject: [PATCH] feat: implement localstorage helper --- src/localStore.ts | 85 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/localStore.ts diff --git a/src/localStore.ts b/src/localStore.ts new file mode 100644 index 0000000..60ab333 --- /dev/null +++ b/src/localStore.ts @@ -0,0 +1,85 @@ +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 + ]; +};