UNPKG

rooks

Version:

Essential React custom hooks ⚓ to super charge your components!

141 lines (140 loc) 5.23 kB
import { useMemo, useState, useEffect, useCallback, useRef } from "react"; // Gets value from localstorage function getValueFromLocalStorage(key) { var _a; if (typeof localStorage === "undefined") { return null; } var storedValue = (_a = localStorage.getItem(key)) !== null && _a !== void 0 ? _a : "null"; try { return JSON.parse(storedValue); } catch (error) { console.error(error); } return storedValue; } // Saves value to localstorage function saveValueToLocalStorage(key, value) { if (typeof localStorage === "undefined") { return null; } return localStorage.setItem(key, JSON.stringify(value)); } /** * @param key Key of the localStorage object * @param initialState Default initial value */ function initialize(key, initialState) { var valueLoadedFromLocalStorage = getValueFromLocalStorage(key); if (valueLoadedFromLocalStorage === null) { return initialState; } else { return valueLoadedFromLocalStorage; } } /** * useLocalstorageState hook * Tracks a value within localStorage and updates it * * @param {string} key - Key of the localStorage object * @param {any} initialState - Default initial value * @see https://react-hooks.org/docs/useLocalstorageState */ function useLocalstorageState(key, initialState) { var _a = useState(function () { return initialize(key, initialState); }), value = _a[0], setValue = _a[1]; var isUpdateFromCrossDocumentListener = useRef(false); var isUpdateFromWithinDocumentListener = useRef(false); var customEventTypeName = useMemo(function () { return "rooks-".concat(key, "-localstorage-update"); }, [key]); useEffect(function () { /** * We need to ensure there is no loop of * storage events fired. Hence we are using a ref * to keep track of whether setValue is from another * storage event */ if (!isUpdateFromCrossDocumentListener.current || !isUpdateFromWithinDocumentListener.current) { saveValueToLocalStorage(key, value); } }, [key, value]); var listenToCrossDocumentStorageEvents = useCallback(function (event) { var _a; if (event.storageArea === localStorage && event.key === key) { try { isUpdateFromCrossDocumentListener.current = true; var newValue = JSON.parse((_a = event.newValue) !== null && _a !== void 0 ? _a : "null"); if (value !== newValue) { setValue(newValue); } } catch (error) { console.log(error); } } }, [key, value]); // check for changes across documents useEffect(function () { // eslint-disable-next-line no-negated-condition if (typeof window !== "undefined") { window.addEventListener("storage", listenToCrossDocumentStorageEvents); return function () { window.removeEventListener("storage", listenToCrossDocumentStorageEvents); }; } else { console.warn("useLocalstorageState: window is undefined."); return function () { }; } }, [listenToCrossDocumentStorageEvents]); var listenToCustomEventWithinDocument = useCallback(function (event) { try { isUpdateFromWithinDocumentListener.current = true; var newValue = event.detail.newValue; if (value !== newValue) { setValue(newValue); } } catch (error) { console.log(error); } }, [value]); // check for changes within document useEffect(function () { // eslint-disable-next-line no-negated-condition if (typeof document !== "undefined") { document.addEventListener(customEventTypeName, listenToCustomEventWithinDocument); return function () { document.removeEventListener(customEventTypeName, listenToCustomEventWithinDocument); }; } else { console.warn("[useLocalstorageState] document is undefined."); return function () { }; } }, [customEventTypeName, listenToCustomEventWithinDocument]); var broadcastValueWithinDocument = useCallback(function (newValue) { // eslint-disable-next-line no-negated-condition if (typeof document !== "undefined") { var event_1 = new CustomEvent(customEventTypeName, { detail: { newValue: newValue } }); document.dispatchEvent(event_1); } else { console.warn("[useLocalstorageState] document is undefined."); } }, [customEventTypeName]); var set = useCallback(function (newValue) { isUpdateFromCrossDocumentListener.current = false; isUpdateFromWithinDocumentListener.current = false; setValue(newValue); broadcastValueWithinDocument(newValue); }, [broadcastValueWithinDocument]); var remove = useCallback(function () { localStorage.removeItem(key); }, [key]); return [value, set, remove]; } export { useLocalstorageState };