potty-chart/src/hooks/localStorage.ts

107 lines
3.3 KiB
TypeScript

let isLocalStorageEnabled = true;
interface MemoryStore {
[key: string]: string;
}
const memoryStorage: MemoryStore = {};
const subscriptions = new EventTarget();
export interface LocalStorageEvent extends CustomEvent {
key: string;
newValue: string;
}
export const getLocalStorage = (key: string): string | null => {
if (!isLocalStorageEnabled) {
return Object.prototype.hasOwnProperty.call(memoryStorage, key) ? memoryStorage[key] : null;
}
try {
const value = window.localStorage.getItem(key);
if (value && memoryStorage[key] !== value) {
memoryStorage[key] = value;
}
return value;
} catch (e) {
// eslint-disable-next-line no-console
console.warn('localStorage is diasabled using memory storage instead.', e);
isLocalStorageEnabled = false;
return getLocalStorage(key);
}
};
export const setLocalStorage = (key: string, newValue: string): void => {
const event = new CustomEvent('local-storage-change', { detail: { key, newValue } });
if (!isLocalStorageEnabled) {
memoryStorage[key] = newValue;
subscriptions.dispatchEvent(event);
return;
}
try {
window.localStorage.setItem(key, newValue);
memoryStorage[key] = newValue;
subscriptions.dispatchEvent(event);
} catch (e) {
// eslint-disable-next-line no-console
console.warn('localStorage is diasabled using memory storage instead.', e);
isLocalStorageEnabled = false;
return setLocalStorage(key, newValue);
}
};
export const isKeySet = (key: string): boolean => {
if (isLocalStorageEnabled) {
try {
for (let i = 0; i < window.localStorage.length; i++) {
if (window.localStorage.key(i) === key) {
return true;
}
}
} catch {
// ignore the error
}
}
return Object.keys(memoryStorage).includes(key);
};
// This function allows you to subscribe to localStorage changes. By default
// this includes changes that are made in other tabs/windows of the same
// browser. The 'callback' will be called whenever a change is made to
// localStorage at the given 'key'. An unsubscibe function is returned, which
// you can call when you are ready to unsubscribe.
export const subscribeToLocalStorage = (
key: string,
callback: (value: string) => void,
options?: { excludeExternal?: boolean }
): (() => void) => {
const { excludeExternal } = options || {};
const updateHandler = (e: LocalStorageEvent | { detail: { key: string; newValue: string } }) => {
const { key: eventKey, newValue } = 'detail' in e ? e.detail : e;
if (eventKey === key) {
callback(newValue);
}
};
// subscribe to events for this instance of the page only
subscriptions.addEventListener(
'local-storage-change',
updateHandler as EventListenerOrEventListenerObject
);
// if not excluded, subscribe to events for all instances of the page
!excludeExternal &&
window.addEventListener('storage', updateHandler as EventListenerOrEventListenerObject);
// return a function that will be used to unsubscribe to these events.
return () => {
subscriptions.removeEventListener(
'local-storage-change',
updateHandler as EventListenerOrEventListenerObject
);
!excludeExternal &&
window.removeEventListener('storage', updateHandler as EventListenerOrEventListenerObject);
};
};