import { Dispatch, SetStateAction, useCallback, useState } from "react"; import { ZodType, ZodTypeDef } from "zod"; type PersistedState = [T, Dispatch>]; type UsePersistedStateOptions = { schema?: ZodType; }; export function usePersistedState( key: string, ): PersistedState; export function usePersistedState( key: string, defaultValue: T, options?: UsePersistedStateOptions, ): PersistedState; export function usePersistedState( key: string, defaultValue?: T, options?: UsePersistedStateOptions, ): PersistedState { const { schema } = options ?? {}; const getValue = () => { try { const value = localStorage.getItem(key); if (value == null) return defaultValue; if (!schema) return JSON.parse(value) as T; return schema.parse(JSON.parse(value)); } catch (error) { return defaultValue; } }; const [value, setValue] = useState(getValue); const [prevKey, setPrevKey] = useState(key); if (prevKey !== key) { setPrevKey(key); setValue(getValue()); } const setValueAndStore: Dispatch> = useCallback( (newValue) => { setValue((prevValue) => { let resolvedValue: T | undefined; if (typeof newValue === "function") { resolvedValue = ( newValue as (prevState: T | undefined) => T | undefined )(prevValue); } else { resolvedValue = newValue; } if (resolvedValue === undefined) { try { localStorage.removeItem(key); } catch (error) { return prevValue; } return defaultValue; } try { localStorage.setItem(key, JSON.stringify(resolvedValue)); } catch (error) { return prevValue; } return resolvedValue; }); }, [key, defaultValue], ); return [value, setValueAndStore]; }