useLocalStorage
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); }
Comments