Files
entscheidomat/hooks/use-local-storage.ts
2026-07-23 21:59:15 +02:00

69 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useCallback, useSyncExternalStore } from "react";
type Listener = () => void;
const listeners = new Map<string, Set<Listener>>();
const cache = new Map<string, unknown>();
function readFromStorage<T>(key: string, initial: T): T {
try {
const raw = window.localStorage.getItem(key);
return raw != null ? (JSON.parse(raw) as T) : initial;
} catch {
return initial;
}
}
function notify(key: string) {
listeners.get(key)?.forEach((l) => l());
}
/**
* State, der in localStorage persistiert wird.
* SSR-sicher über useSyncExternalStore: liefert serverseitig `initial`,
* hydriert clientseitig aus localStorage.
*/
export function useLocalStorage<T>(key: string, initial: T) {
const subscribe = useCallback(
(callback: Listener) => {
let set = listeners.get(key);
if (!set) {
set = new Set();
listeners.set(key, set);
}
set.add(callback);
return () => set!.delete(callback);
},
[key],
);
const getSnapshot = useCallback(() => {
if (!cache.has(key)) cache.set(key, readFromStorage(key, initial));
return cache.get(key) as T;
// eslint-disable-next-line react-hooks/exhaustive-deps -- `initial` is only used as a one-time fallback
}, [key]);
const getServerSnapshot = useCallback(() => initial, [initial]);
const value = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
const setValue = useCallback(
(next: T | ((prev: T) => T)) => {
const prev = cache.has(key) ? (cache.get(key) as T) : readFromStorage(key, initial);
const resolved = typeof next === "function" ? (next as (p: T) => T)(prev) : next;
cache.set(key, resolved);
try {
window.localStorage.setItem(key, JSON.stringify(resolved));
} catch {
// Speicher voll o.ä. egal
}
notify(key);
},
[key, initial],
);
return [value, setValue] as const;
}