useLocalStorage
Persist state to localStorage with automatic serialization
1// useLocalStorage - Sync state with localStorage2import { useState, useEffect, useCallback } from 'react';34function useLocalStorage<T>(5 key: string,6 initialValue: T,7 options?: {8 serializer?: (value: T) => string;9 deserializer?: (value: string) => T;10 }11): [T, (value: T | ((val: T) => T)) => void, () => void] {12 const {13 serializer = JSON.stringify,14 deserializer = JSON.parse15 } = options || {};1617 // Get initial value from localStorage or use provided initial value18 const [storedValue, setStoredValue] = useState<T>(() => {19 try {20 if (typeof window === 'undefined') {21 return initialValue;22 }2324 const item = window.localStorage.getItem(key);25 return item ? deserializer(item) : initialValue;26 } catch (error) {27 console.error(`Error loading localStorage key "${key}":`, error);28 return initialValue;29 }30 });3132 // Update localStorage when state changes33 const setValue = useCallback((value: T | ((val: T) => T)) => {34 try {35 const valueToStore = value instanceof Function ? value(storedValue) : value;36 setStoredValue(valueToStore);3738 if (typeof window !== 'undefined') {39 window.localStorage.setItem(key, serializer(valueToStore));4041 // Dispatch storage event for cross-tab synchronization42 window.dispatchEvent(new StorageEvent('storage', {43 key,44 newValue: serializer(valueToStore),45 url: window.location.href,46 storageArea: window.localStorage47 }));48 }49 } catch (error) {50 console.error(`Error saving to localStorage key "${key}":`, error);51 }52 }, [key, serializer, storedValue]);5354 // Remove value from localStorage55 const removeValue = useCallback(() => {56 try {57 setStoredValue(initialValue);58 if (typeof window !== 'undefined') {59 window.localStorage.removeItem(key);60 }61 } catch (error) {62 console.error(`Error removing localStorage key "${key}":`, error);63 }64 }, [key, initialValue]);6566 // Listen for changes in other tabs67 useEffect(() => {68 const handleStorageChange = (e: StorageEvent) => {69 if (e.key === key && e.newValue) {70 try {71 setStoredValue(deserializer(e.newValue));72 } catch (error) {73 console.error(`Error parsing localStorage update for key "${key}":`, error);74 }75 }76 };7778 window.addEventListener('storage', handleStorageChange);79 return () => window.removeEventListener('storage', handleStorageChange);80 }, [key, deserializer]);8182 return [storedValue, setValue, removeValue];83}8485// Usage example86function Settings() {87 const [theme, setTheme, resetTheme] = useLocalStorage('theme', 'light');88 const [preferences, setPreferences] = useLocalStorage('userPrefs', {89 notifications: true,90 autoSave: false,91 language: 'en'92 });9394 return (95 <div>96 <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>97 Toggle Theme: {theme}98 </button>99100 <button onClick={() => setPreferences(prev => ({101 ...prev,102 notifications: !prev.notifications103 }))}>104 Notifications: {preferences.notifications ? 'On' : 'Off'}105 </button>106107 <button onClick={resetTheme}>Reset Theme</button>108 </div>109 );110}