From fcb05f46961dbe5ba765b358247e31ce7396b64a Mon Sep 17 00:00:00 2001 From: Sebastian Tilsch Date: Wed, 15 May 2024 19:11:59 +0200 Subject: [PATCH] fix local storage build error by including the function directly rather then importing it --- packages/state-hooks/package.json | 1 - packages/state-hooks/src/useLocalSettings.ts | 2 +- packages/state-hooks/src/useLocalStorage.ts | 115 +++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 packages/state-hooks/src/useLocalStorage.ts diff --git a/packages/state-hooks/package.json b/packages/state-hooks/package.json index ce361497..07ff847a 100644 --- a/packages/state-hooks/package.json +++ b/packages/state-hooks/package.json @@ -53,7 +53,6 @@ "@slub/edb-ui-utils": "workspace:*", "@slub/sparql-schema": "workspace:*", "@reduxjs/toolkit": "^2.2.3", - "use-local-storage": "^3.0.0", "json-schema": "^0.4.0", "jsonld": "^8.3.2", "react-redux": "^9.1.1", diff --git a/packages/state-hooks/src/useLocalSettings.ts b/packages/state-hooks/src/useLocalSettings.ts index 8d9030f2..452aea94 100644 --- a/packages/state-hooks/src/useLocalSettings.ts +++ b/packages/state-hooks/src/useLocalSettings.ts @@ -10,7 +10,7 @@ import { UseLocalSettings, } from "@slub/edb-core-types"; import { useAdbContext } from "./provider"; -import useLocalStorage from "use-local-storage"; +import { useLocalStorage } from "./useLocalStorage"; const defaultSparqlEndpoints: SparqlEndpoint[] = [ { diff --git a/packages/state-hooks/src/useLocalStorage.ts b/packages/state-hooks/src/useLocalStorage.ts new file mode 100644 index 00000000..6d92ace2 --- /dev/null +++ b/packages/state-hooks/src/useLocalStorage.ts @@ -0,0 +1,115 @@ +import { useEffect, useMemo, useRef, useState } from "react"; + +type Serializer = (object: T | undefined) => string; +type Parser = (val: string) => T | undefined; +type Setter = React.Dispatch>; + +type Options = Partial<{ + serializer: Serializer; + parser: Parser; + logger: (error: any) => void; + syncData: boolean; +}>; + +export function useLocalStorage( + key: string, + defaultValue: T, + options?: Options, +): [T, Setter]; +export function useLocalStorage( + key: string, + defaultValue?: T, + options?: Options, +) { + const opts = useMemo(() => { + return { + serializer: JSON.stringify, + parser: JSON.parse, + logger: console.log, + syncData: true, + ...options, + }; + }, [options]); + + const { serializer, parser, logger, syncData } = opts; + + const rawValueRef = useRef(null); + + const [value, setValue] = useState(() => { + if (typeof window === "undefined") return defaultValue; + + try { + rawValueRef.current = window.localStorage.getItem(key); + const res: T = rawValueRef.current + ? parser(rawValueRef.current) + : defaultValue; + return res; + } catch (e) { + logger(e); + return defaultValue; + } + }); + + useEffect(() => { + if (typeof window === "undefined") return; + + const updateLocalStorage = () => { + // Browser ONLY dispatch storage events to other tabs, NOT current tab. + // We need to manually dispatch storage event for current tab + if (value !== undefined) { + const newValue = serializer(value); + const oldValue = rawValueRef.current; + rawValueRef.current = newValue; + window.localStorage.setItem(key, newValue); + window.dispatchEvent( + new StorageEvent("storage", { + storageArea: window.localStorage, + url: window.location.href, + key, + newValue, + oldValue, + }), + ); + } else { + window.localStorage.removeItem(key); + window.dispatchEvent( + new StorageEvent("storage", { + storageArea: window.localStorage, + url: window.location.href, + key, + }), + ); + } + }; + + try { + updateLocalStorage(); + } catch (e) { + logger(e); + } + }, [value]); + + useEffect(() => { + if (!syncData) return; + + const handleStorageChange = (e: StorageEvent) => { + if (e.key !== key || e.storageArea !== window.localStorage) return; + + try { + if (e.newValue !== rawValueRef.current) { + rawValueRef.current = e.newValue; + setValue(e.newValue ? parser(e.newValue) : undefined); + } + } catch (e) { + logger(e); + } + }; + + if (typeof window === "undefined") return; + + window.addEventListener("storage", handleStorageChange); + return () => window.removeEventListener("storage", handleStorageChange); + }, [key, syncData]); + + return [value, setValue]; +}