Files
pangolin/src/hooks/useLocalStorage.ts
Fred KISSIE 2f1abfbef8 🚧 New version popup
2025-11-05 06:55:08 +01:00

100 lines
3.2 KiB
TypeScript

import {
useState,
useEffect,
useCallback,
Dispatch,
SetStateAction
} from "react";
type SetValue<T> = Dispatch<SetStateAction<T>>;
export function useLocalStorage<T>(
key: string,
initialValue: T
): [T, SetValue<T>] {
// Get initial value from localStorage or use the provided initial value
const readValue = useCallback((): T => {
// Prevent build error "window is undefined" during SSR
if (typeof window === "undefined") {
return initialValue;
}
try {
const item = window.localStorage.getItem(key);
return item ? (JSON.parse(item) as T) : initialValue;
} catch (error) {
console.warn(`Error reading localStorage key "${key}":`, error);
return initialValue;
}
}, [initialValue, key]);
// State to store our value
const [storedValue, setStoredValue] = useState<T>(readValue);
// Return a wrapped version of useState's setter function that
// persists the new value to localStorage
const setValue: SetValue<T> = useCallback(
(value) => {
// Prevent build error "window is undefined" during SSR
if (typeof window === "undefined") {
console.warn(
`Tried setting localStorage key "${key}" even though environment is not a client`
);
}
try {
// Allow value to be a function so we have the same API as useState
const newValue =
value instanceof Function ? value(storedValue) : value;
// Save to local storage
window.localStorage.setItem(key, JSON.stringify(newValue));
// Save state
setStoredValue(newValue);
// Dispatch a custom event so every useLocalStorage hook is notified
window.dispatchEvent(new Event("local-storage"));
} catch (error) {
console.warn(`Error setting localStorage key "${key}":`, error);
}
},
[key, storedValue]
);
// Listen for changes to this key from other tabs/windows
useEffect(() => {
const handleStorageChange = (e: StorageEvent) => {
if (e.key === key && e.newValue !== null) {
try {
setStoredValue(JSON.parse(e.newValue));
} catch (error) {
console.warn(
`Error parsing localStorage value for key "${key}":`,
error
);
}
}
};
// Listen for storage events (changes from other tabs)
window.addEventListener("storage", handleStorageChange);
// Listen for custom event (changes from same tab)
const handleLocalStorageChange = () => {
setStoredValue(readValue());
};
window.addEventListener("local-storage", handleLocalStorageChange);
return () => {
window.removeEventListener("storage", handleStorageChange);
window.removeEventListener(
"local-storage",
handleLocalStorageChange
);
};
}, [key, readValue]);
return [storedValue, setValue];
}