bananas-commerce-admin
Version:
What's this, an admin for apes?
78 lines (67 loc) • 2.48 kB
text/typescript
import { useEffect, useState } from "react";
/**
* The `useStorage` hook is a generic hook for reactively storing state in a
* similar fashion to reacts usual {@link useState} hook. There is however some
* caveats which are good to keep in mind:
* - Any value of type `T` needs to be JSON (de-)serializeable. That means
* storing certain non-serializeable values can result in unexpected
* behavior, for example storing `Date` objects will stringify them.
* - Setting the value to `undefined` is equivalent to deleting the storage
* entry, not storing an `undefined` value. The reasoning behind this is
* that because JSON doesn't support `undefined` it should instead indicate
* an `undefined` value.
* - The hook is designed to listen to the global `storage` event which
* should make state changes propagate within the same context. If the
* storage is for example modified from another tab that change should
* trigger a state update in the current tab too. The behaviour of this may
* be browser dependant.
*/
export function useStorage<T>(
storage: Storage,
key: string,
initial: T,
): [T, React.Dispatch<React.SetStateAction<T>>] {
const stored = storage.getItem(key);
const value = stored === null ? initial : JSON.parse(stored);
const [state, setState] = useState(value);
useEffect(() => {
if (state === undefined) {
storage.removeItem(key);
} else {
storage.setItem(key, JSON.stringify(state));
}
}, [state]);
useEffect(() => {
const listener = (event: StorageEvent) => {
if (event.storageArea !== storage) {
return;
}
if (event.key === null) {
setState(initial);
return;
}
if (event.key === key && event.newValue !== event.oldValue && event.newValue !== state) {
setState(event.newValue !== null ? JSON.parse(event.newValue) : null);
}
};
addEventListener("storage", listener);
return () => removeEventListener("storage", listener);
}, [storage]);
return [state, setState];
}
/**
* A hook for storing react state in `localStorage`.
*
* @see useStorage
*/
export function useLocalStorage<T>(key: string, initial: T) {
return useStorage(localStorage, key, initial);
}
/**
* A hook for storing react state in `sessionStorage`.
*
* @see useStorage
*/
export function useSessionStorage<T>(key: string, initial?: T) {
return useStorage(sessionStorage, key, initial);
}