@navinc/base-react-components
Version:
Nav's Pattern Library
81 lines • 3.77 kB
JavaScript
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