import { useEffect, useRef, useState } from "react"; import { isFunction, stringify } from "lib/utils"; const hasWindow = () => { return typeof window !== "undefined" ? true : false; }; const hasLocalStorage = () => { return hasWindow() && window.localStorage ? true : false; }; const getItem = (key, ttl = 0) => { if (!hasLocalStorage()) return null; let value = window.localStorage.getItem(key); if (!value) return null; const now = new Date()?.getTime(); const payload = JSON.parse(value); if (payload?.expiry > 0 && now > payload?.expiry) { removeItem(key); return null; } return payload?.value || null; }; const setItem = (key, value, ttl = 0) => { if (!hasLocalStorage()) return false; const now = new Date()?.getTime(); const payload = { value: value, expiry: (ttl && now + ttl) || 0 }; return window.localStorage.setItem(key, JSON.stringify(payload)); }; const removeItem = (key) => { if (!hasLocalStorage()) return null; return window.localStorage.removeItem(key); }; // use local storage to store state values for persistent values export function useLocalStorage(key, initialValue, ttl = 0) { const [storedValue, setStoredValue] = useState(() => { try { const item = getItem(key, ttl); return (item && JSON.parse(item)) || initialValue; } catch (err) { log.error("useLocalStorage useState error", err.message || err); return initialValue; } }); const setValue = (value) => { try { const item = isFunction(value) ? value(storedValue) : value; setStoredValue(item); setItem(key, stringify(item), ttl); } catch (err) { log.error("useLocalStorage setValue error", err.message || err); } }; return [storedValue, setValue]; } export function useStateCallback(initialState) { const [state, setState] = useState(initialState); const cbRef = useRef(null); // mutable ref to store current callback const setStateCallback = (state, cb) => { cbRef.current = cb; // store passed callback to ref setState(state); }; useEffect(() => { // cb.current is `null` on initial render, so we only execute cb on state *updates* if (cbRef.current) { cbRef.current(state); cbRef.current = null; // reset callback after execution } }, [state]); return [state, setStateCallback]; }