diff --git a/index.js b/index.js index 88546e2..d8c063e 100644 --- a/index.js +++ b/index.js @@ -40,6 +40,10 @@ function throttle(cb, ms) { }; } +const dispatchStorageEvent = (key, newValue) => { + window.dispatchEvent(new StorageEvent("storage", { key, newValue })); +}; + export function useBattery() { const [state, setState] = React.useState({ supported: true, @@ -1431,6 +1435,68 @@ export function useScript(src, options = {}) { return status; } +const setSessionStorageItem = (key, value) => { + const stringifiedValue = JSON.stringify(value); + window.sessionStorage.setItem(key, stringifiedValue); + dispatchStorageEvent(key, stringifiedValue); +}; + +const removeSessionStorageItem = (key) => { + window.sessionStorage.removeItem(key); + dispatchStorageEvent(key, null); +}; + +const getSessionStorageItem = (key) => { + return window.sessionStorage.getItem(key); +}; + +const useSessionStorageSubscribe = (callback) => { + window.addEventListener("storage", callback); + return () => window.removeEventListener("storage", callback); +}; + +const getSessionStorageServerSnapshot = () => { + throw Error("useSessionStorage is a client-only hook"); +}; + +export function useSessionStorage(key, initialValue) { + const getSnapshot = () => getSessionStorageItem(key); + + const store = React.useSyncExternalStore( + useSessionStorageSubscribe, + getSnapshot, + getSessionStorageServerSnapshot + ); + + const setState = React.useCallback( + (v) => { + try { + const nextState = typeof v === "function" ? v(JSON.parse(store)) : v; + + if (nextState === undefined || nextState === null) { + removeSessionStorageItem(key); + } else { + setSessionStorageItem(key, nextState); + } + } catch (e) { + console.warn(e); + } + }, + [key, store] + ); + + React.useEffect(() => { + if ( + getSessionStorageItem(key) === null && + typeof initialValue !== "undefined" + ) { + setSessionStorageItem(key, initialValue); + } + }, [key, initialValue]); + + return [store ? JSON.parse(store) : initialValue, setState]; +} + export function useSet(values) { const setRef = React.useRef(new Set(values)); const [, reRender] = React.useReducer((x) => x + 1, 0);