import { useCallback, useState } from 'react'; export function useLocalStorage( key: string, initialValue: T, options?: { /** * If `true`, value can be set to `undefined` and clearing the entry will set state to `undefined` */ allowUndefined?: T extends undefined ? boolean : never; /** * A function that alters the behavior of the stringification process of `JSON.stringify` * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter */ replacer?: (key: string, value: any) => any; /** * A function that tranforms the value computed by `JSON.parse` * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#the_reviver_parameter */ reviver?: (key: string, value: any) => any; }, ) { const [storedValue, setStoredValue] = useState(() => { if (typeof window === 'undefined') return initialValue; let item; try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item, options?.reviver) : initialValue; } catch (error) { return typeof item === 'string' ? item : initialValue; } }); const setValue = useCallback( (value: T | ((val: T) => T)) => { const valueToStore = value instanceof Function ? value(storedValue) : value; setStoredValue(options?.allowUndefined ? (valueToStore as T) : valueToStore ?? initialValue); if (typeof window !== 'undefined') { if (valueToStore === undefined) window.sessionStorage.removeItem(key); else window.localStorage.setItem( key, typeof valueToStore === 'string' ? valueToStore : JSON.stringify(valueToStore, options?.replacer), ); } }, [storedValue], // eslint-disable-line react-hooks/exhaustive-deps ); /** * Clears the entry in the local storage and sets state to initial value or `undefined` if allowed */ const clear = useCallback(() => { window.localStorage.removeItem(key); setStoredValue(options?.allowUndefined ? (undefined as T) : initialValue); }, []); // eslint-disable-line react-hooks/exhaustive-deps return [storedValue, setValue, clear] as const; }