UNPKG

@geee-be/react-utils

Version:

A collection of powerful, type-safe React hooks and utility functions. Includes state management, SSR-compatible hooks, device detection, and browser API wrappers.

57 lines (48 loc) 1.91 kB
'use client'; import type { Dispatch, SetStateAction } from 'react'; import { useCallback, useEffect, useState } from 'react'; import type { InitialValue, SerializationOptions } from './state.util.js'; import { deserialize, getInitialValue, serialize } from './state.util.js'; type SetValue<T> = Dispatch<SetStateAction<T>>; // Provides hook that persist the state with local storage with sync'd updates // NOTE: this only syncs between different window contexts, it doesn't sync between components in the same window export const useLocalState = <T, S = T>( key: string, initialValue: InitialValue<T>, options?: SerializationOptions<T, S>, ): [T, SetValue<T>] => { const readValue = useCallback((): T => { const item = localStorage.getItem(key); return ( deserialize<T, S>(item, options?.fromSerializable) ?? getInitialValue(initialValue) ); }, [key, options?.fromSerializable, initialValue]); const [storedValue, setStoredValue] = useState<T>(readValue); const setValue: SetValue<T> = useCallback( (value) => { const newValue = value instanceof Function ? value(storedValue) : value; setStoredValue(newValue); localStorage.setItem( key, serialize<T, S>(newValue, options?.toSerializable), ); }, [key, options?.toSerializable, storedValue], ); const storageValueChanged = useCallback( (ev: StorageEvent) => { if (ev.storageArea !== localStorage || ev.key !== key) return; setStoredValue( deserialize<T, S>(ev.newValue, options?.fromSerializable) ?? getInitialValue(initialValue), ); }, [key, options?.fromSerializable, initialValue], ); useEffect(() => { window.addEventListener('storage', storageValueChanged); return () => window.removeEventListener('storage', storageValueChanged); }, [storageValueChanged]); return [storedValue, setValue]; };