mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-03 00:29:10 +00:00
100 lines
3.2 KiB
TypeScript
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];
|
|
}
|