usehooks
usehooks copied to clipboard
useLocalStorageDictionary
This could be used for storing a dictionary on a key in local storage. It is built upon your useLocalStorageHook. Note the effect which updates the exposed value when the nestedKey changes.
import { useCallback, useEffect, useMemo, useState } from "react";
export const useLocalStorageDictionary = <T>({
mainKey,
nestedKey,
initialVal,
}: useLocalStorageDictionaryArgs<T>): [T, VoidReceives<UpdateFuncVal<T>>] => {
const [exposedValue, setExposedValue] = useState(() =>
fetchNested<T>({ mainKey, nestedKey, initialVal, editValue })
);
const editValue = useMemo(
() => createEditOrInitValue(mainKey, nestedKey, initialVal),
[mainKey, nestedKey, initialVal]
);
const updateNested = useCallback(
(value: UpdateFuncVal<T>): void => {
try {
const newExposedValue =
value instanceof Function ? value(exposedValue) : value;
setExposedValue(newExposedValue);
const mainValue = JSON.parse(
window.localStorage.getItem(mainKey) || "{}"
);
editValue({ nestedValue: newExposedValue, mainValue });
} catch (error) {
console.error(error);
}
},
[mainKey, exposedValue, editValue]
);
useEffect(() => {
setExposedValue(fetchNested({ mainKey, nestedKey, initialVal, editValue }));
}, [nestedKey, mainKey, initialVal, editValue]);
return [exposedValue, updateNested];
};
const fetchNested = <T>({
mainKey,
nestedKey,
initialVal,
editValue,
}: useLocalStorageDictionaryArgs<T> & { editValue: EditOrInitValue }): T => {
try {
const main = window.localStorage.getItem(mainKey);
if (!main) {
editValue({});
return initialVal;
}
const parsedMain = JSON.parse(main);
const nested = parsedMain[nestedKey];
if (!nested)
editValue({
mainValue: parsedMain,
});
return nested ?? initialVal;
} catch (error) {
console.error(error);
return initialVal;
}
};
const createEditOrInitValue = <T>(
mainKey: string,
nestedKey: string,
initialVal: T
): EditOrInitValue => ({
mainValue = {},
nestedValue = initialVal,
}): void =>
localStorage.setItem(
mainKey,
JSON.stringify({ ...mainValue, [nestedKey]: nestedValue })
);
type VoidReceives<T> = (t: T) => void;
type EditOrInitValue = VoidReceives<{
mainValue?: Record<string, unknown>;
nestedValue?: unknown;
}>;
type useLocalStorageDictionaryArgs<T> = {
mainKey: string;
nestedKey: string;
initialVal: T;
};
type UpdateFuncVal<T> = T | ((t: T) => T);