useLocalStorage

PHOTO EMBED

Wed Sep 15 2021 07:24:25 GMT+0000 (Coordinated Universal Time)

Saved by @cxgarcia #typescript

import { useReducer } from "react";
import { debounce } from "utils";
import localStorageReducer from "./useLocalStorage.reducer";
import { _getState, _setLocalStorage } from "./useLocalStorage.utils";

export default function useLocalStorage(
  defaultKey = "default",
  defaultInitialState = {}
) {
  const [state, dispatch] = useReducer(
    localStorageReducer,
    null,
    getInitialState
  );

  function getInitialState() {
    const _state = _getState(defaultKey, defaultInitialState, {
      defaultKey,
    });

    _setLocalStorage(defaultKey, _state);

    return _state;
  }

  return [state, dispatch, defaultInitialState];
}

export function _createStorage(dispatch) {
  return (lsKey, state) => {
    dispatch({
      type: "CREATE_STORAGE",
      lsKey,
      state,
    });
  };
}

export function _updateStorageByKey(dispatch) {
  return (stateKey, state) => {
    dispatch({
      type: "UPDATE_STORAGE_BY_KEY",
      state,
      stateKey,
    });
  };
}

export function _updateStorage(dispatch) {
  return (state) => {
    dispatch({
      type: "UPDATE_STORAGE",
      state,
    });
  };
}

export function _deleteStorage(dispatch) {
  return (lsKey) => {
    dispatch({
      type: "DELETE_STORAGE",
      lsKey,
    });
  };
}

export function _updateKey(dispatch) {
  return (lsKey, stateUpdates = {}) => {
    dispatch({
      type: "UPDATE_KEY",
      lsKey,
      stateUpdates,
    });
  };
}

export function _setCurrentKey(dispatch) {
  return (lsKey) => {
    dispatch({
      type: "SET_CURRENT_KEY",
      lsKey,
    });
  };
}

import {
  _getState,
  _setLocalStorage,
  _clearLocalStorage,
  _debouncedSetLocalStorage,
} from "./useLocalStorage.utils";

export default function localStorageReducer(state, action) {
  switch (action.type) {
    case "CREATE_STORAGE": {
      const { lsKey, state: defaultState } = action;
      const { defaultKey } = state;

      const _state = _getState(lsKey, defaultState, { defaultKey });

      _setLocalStorage(lsKey, _state);

      return _state;
    }

    case "UPDATE_STORAGE": {
      const { lsKey } = state;
      const { state: _state } = action;

      const updatedState = {
        ...state,
        ..._state,
      };

      //since certain actions cause a substantial amount of rerenders per ms, it's best to defer access to local storage by debouncing
      _debouncedSetLocalStorage(lsKey, updatedState);

      return updatedState;
    }

    case "UPDATE_STORAGE_BY_KEY": {
      const { lsKey } = state;
      const { state: _state, stateKey } = action;

      const updatedState = {
        ...state,
        [stateKey]: _state,
      };

      _debouncedSetLocalStorage(lsKey, updatedState);

      return updatedState;
    }

    case "DELETE_STORAGE": {
      const { lsKey } = action;
      const { defaultKey } = state;

      if (lsKey === defaultKey) return state;

      _clearLocalStorage(lsKey);

      return _getState(defaultKey);
    }

    case "SET_CURRENT_KEY": {
      const { lsKey } = action;

      return _getState(lsKey);
    }

    //update keys in KEYS
    case "UPDATE_KEY": {
      const { stateUpdates, lsKey } = action;
      const { lsKey: lsKeyPrev, defaultKey } = state;

      if (lsKey === lsKeyPrev) return state;
      if (lsKeyPrev !== defaultKey) _clearLocalStorage(lsKeyPrev); //clear storage with previous key, unless it is the default key

      const _state = _getState(lsKey, state, { ...stateUpdates, lsKey });
      _setLocalStorage(lsKey, _state);

      return _state;
    }

    //useful for when you don't want to update the localStorage
    case "UPDATE_STATE": {
      return action.state;
    }

    default: {
      throw new Error(`Unhandled action type: ${action.type}`);
    }
  }
}


export function _getState(lsKey, defaultState, overwrites = {}) {
  const _state = _getLocalStorage(lsKey, defaultState);

  const state = { ..._state, ...overwrites, lsKey };

  return state;
}

export function _getKeys(key) {
  const _keys = Object.keys(localStorage);
  return _keys.includes(key) ? _keys : [key, ..._keys];
}

export function _getLocalStorage(key, defaultState) {
  const valueInLocalStorage = window.localStorage.getItem(key);
  return JSON.parse(valueInLocalStorage) ?? defaultState;
}

export function _setLocalStorage(key, state) {
  window.localStorage.setItem(key, JSON.stringify(state));
}

export const _debouncedSetLocalStorage = debounce(_setLocalStorage, 100);

export function _clearLocalStorage(key) {
  window.localStorage.removeItem(key);
}
content_copyCOPY