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);
}
Preview:
downloadDownload PNG
downloadDownload JPEG
downloadDownload SVG
Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!
Click to optimize width for Twitter