usehooks icon indicating copy to clipboard operation
usehooks copied to clipboard

useLocalStorageDictionary

Open netanel-haber opened this issue 4 years ago • 0 comments

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);

netanel-haber avatar Dec 29 '20 15:12 netanel-haber