mod-arch-core
Version:
Core functionality and API utilities for modular architecture micro-frontend projects
97 lines • 4.12 kB
JavaScript
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