UNPKG

@navinc/base-react-components

Version:
81 lines 3.77 kB
import { isBrowser, noop } from '@navinc/utils'; import { useCallback, useState, useRef, useLayoutEffect } from 'react'; export const useLocalStorage = (key, initialValue, options) => { if (!isBrowser()) { return [initialValue, noop, noop]; } if (!key) { throw new Error('useLocalStorage key may not be falsy'); } // eslint-disable-next-line no-nested-ternary -- nesting isn't too bad here const deserializer = options ? (options.raw ? (value) => value : options.deserializer) : JSON.parse; // eslint-disable-next-line react-hooks/rules-of-hooks -- conditional calls will be consistent across renders or an error will be thrown const initializer = useRef((key) => { try { // eslint-disable-next-line no-nested-ternary -- nesting isn't too bad here const serializer = options ? (options.raw ? String : options.serializer) : JSON.stringify; const localStorageValue = localStorage.getItem(key); if (localStorageValue !== null) { return deserializer(localStorageValue); } else { if (initialValue) { localStorage.setItem(key, serializer(initialValue)); } return initialValue; } } catch (_a) { // If user is in private mode or has storage restriction // localStorage can throw. JSON.parse and JSON.stringify // can throw, too. return initialValue; } }); // eslint-disable-next-line react-hooks/rules-of-hooks -- conditional calls will be consistent across renders or an error will be thrown const [state, setState] = useState(() => initializer.current(key)); // eslint-disable-next-line react-hooks/rules-of-hooks -- conditional calls will be consistent across renders or an error will be thrown useLayoutEffect(() => setState(initializer.current(key)), [key]); // eslint-disable-next-line react-hooks/rules-of-hooks -- conditional calls will be consistent across renders or an error will be thrown const set = useCallback((valOrFunc) => { try { const newState = typeof valOrFunc === 'function' ? valOrFunc(state) : valOrFunc; if (typeof newState === 'undefined') return; let value; if (options) if (options.raw) if (typeof newState === 'string') value = newState; else value = JSON.stringify(newState); else if (options.serializer) value = options.serializer(newState); else value = JSON.stringify(newState); else value = JSON.stringify(newState); localStorage.setItem(key, value); setState(deserializer(value)); } catch (_a) { // If user is in private mode or has storage restriction // localStorage can throw. Also JSON.stringify can throw. } }, // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: document why this is disabled [key, setState]); // eslint-disable-next-line react-hooks/rules-of-hooks -- conditional calls will be consistent across renders or an error will be thrown const remove = useCallback(() => { try { localStorage.removeItem(key); setState(undefined); } catch (_a) { // If user is in private mode or has storage restriction // localStorage can throw. } }, [key, setState]); return [state, set, remove]; }; //# sourceMappingURL=use-local-storage.js.map