@qazuor/react-hooks
Version:
A comprehensive collection of production-ready React hooks for modern web applications. Features type-safe implementations, extensive testing, and zero dependencies. Includes hooks for state management, browser APIs, user interactions, and development uti
102 lines (92 loc) • 3.38 kB
text/typescript
import { useCallback, useEffect, useRef, useState } from 'react';
interface StorageOptions<T> {
/** Serializer function to convert value to string */
serializer?: (value: T) => string;
/** Deserializer function to parse stored string */
deserializer?: (value: string) => T;
/** Error handler function */
onError?: (error: Error) => void;
/** Whether to sync across browser tabs */
syncTabs?: boolean;
}
/**
* useSessionStorage
*
* @description A hook that synchronizes a stateful value with the browser's Session Storage.
* Similar to `useLocalStorage`, but it uses `sessionStorage` under the hood.
* Data stored in Session Storage is cleared after the session ends (e.g., when the browser tab is closed).
*
* @typeParam T - The type of the value to store.
* @param {string} key - The key under which the value is stored in Session Storage.
* @param {T} initialValue - The default value if none is found in Session Storage.
* @param {StorageOptions<T>} [options] - Optional configuration.
* @returns {[T, (value: T | ((val: T) => T)) => void]} A tuple with:
* - The current stored value.
* - A setter function that updates both the state and Session Storage.
*
* @example
* ```ts
* const [sessionData, setSessionData] = useSessionStorage('session-key', { foo: 'bar' });
* ```
*/
export function useSessionStorage<T>(key: string, initialValue: T, options: StorageOptions<T> = {}) {
const {
serializer = JSON.stringify,
deserializer = JSON.parse,
onError = console.error,
syncTabs = false
} = options;
const mounted = useRef(true);
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.sessionStorage.getItem(key);
return item ? deserializer(item) : initialValue;
} catch (error) {
onError(error as Error);
return initialValue;
}
});
const setValue = useCallback(
(value: T | ((val: T) => T)) => {
setStoredValue((prev) => {
try {
const newValue = value instanceof Function ? value(prev) : value;
window.sessionStorage.setItem(key, serializer(newValue));
return newValue;
} catch (error) {
onError(error as Error);
return prev;
}
});
},
[key, serializer, onError]
);
const handleStorageChange = useCallback(
(event: StorageEvent) => {
if (event.key === key && event.newValue !== null && mounted.current) {
try {
const newValue = deserializer(event.newValue);
setStoredValue(newValue);
} catch (error) {
onError(error as Error);
}
}
},
[key, deserializer, onError]
);
useEffect(() => {
if (syncTabs) {
window.addEventListener('storage', handleStorageChange);
return () => {
window.removeEventListener('storage', handleStorageChange);
};
}
}, [syncTabs, handleStorageChange]);
useEffect(
() => () => {
mounted.current = false;
},
[]
);
return [storedValue, setValue] as const;
}