UNPKG

mod-arch-core

Version:

Core functionality and API utilities for modular architecture micro-frontend projects

97 lines 4.12 kB
import * as React from 'react'; import { useDeepCompareMemoize } from '../utilities/useDeepCompareMemoize'; import { useEventListener } from '../utilities/useEventListener'; const BrowserStorageContext = React.createContext({ getValue: () => null, setJSONValue: () => false, setStringValue: () => undefined, }); /** * useBrowserStorage will handle all the effort behind managing localStorage or sessionStorage. */ export const useBrowserStorage = (storageKey, defaultValue, jsonify = true, isSessionStorage = false) => { const { getValue, setJSONValue, setStringValue } = React.useContext(BrowserStorageContext); const setValue = React.useCallback((value) => { if (jsonify) { return setJSONValue(storageKey, value, isSessionStorage); } if (typeof value === 'string') { setStringValue(storageKey, value, isSessionStorage); return true; } /* eslint-disable-next-line no-console */ console.error('Was not a string value provided, cannot stringify'); return false; }, [isSessionStorage, jsonify, setJSONValue, setStringValue, storageKey]); const value = useDeepCompareMemoize( // eslint-disable-next-line @typescript-eslint/consistent-type-assertions getValue(storageKey, jsonify, isSessionStorage) ?? defaultValue); return [value, setValue]; }; const getStorage = (isSessionStorage) => { if (isSessionStorage) { return sessionStorage; } return localStorage; }; /** * @see useBrowserStorage */ export const BrowserStorageContextProvider = ({ children, }) => { const [values, setValues] = React.useState({}); /** * Only listen to other storage changes (windows/tabs) -- which are localStorage. * Session storage does not have cross instance storages. * See MDN for more: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage */ useEventListener(window, 'storage', () => { // Another browser tab has updated storage, sync up the data const keys = Object.keys(values); setValues(keys.reduce((acc, key) => { const value = localStorage.getItem(key); return { ...acc, [key]: value }; }, {})); }); const getValue = React.useCallback((key, parseJSON, isSessionStorage = false) => { const value = getStorage(isSessionStorage).getItem(key); if (value === null) { return value; } if (parseJSON) { try { return JSON.parse(value); } catch { /* eslint-disable-next-line no-console */ console.warn(`Failed to parse storage value "${key}"`); return null; } } else { return value; } }, []); const setJSONValue = React.useCallback((storageKey, value, isSessionStorage = false) => { try { const storageValue = JSON.stringify(value); getStorage(isSessionStorage).setItem(storageKey, storageValue); setValues((oldValues) => ({ ...oldValues, [storageKey]: storageValue })); return true; } catch { /* eslint-disable-next-line no-console */ console.warn('Could not store a value because it was requested to be stringified but was an invalid value for stringification.'); return false; } }, []); const setStringValue = React.useCallback((storageKey, value, isSessionStorage = false) => { getStorage(isSessionStorage).setItem(storageKey, value); setValues((oldValues) => ({ ...oldValues, [storageKey]: value })); }, []); const contextValue = React.useMemo(() => ({ getValue, setJSONValue, setStringValue }), // Also trigger a context update if `values` changes. // eslint-disable-next-line react-hooks/exhaustive-deps [getValue, setJSONValue, setStringValue, values]); return (React.createElement(BrowserStorageContext.Provider, { value: contextValue }, children)); }; //# sourceMappingURL=BrowserStorageContext.js.map