state.js 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import { useEffect, useRef, useState } from "react";
  2. import { isFunction, stringify } from "lib/utils";
  3. const hasWindow = () => {
  4. return typeof window !== "undefined" ? true : false;
  5. };
  6. const hasLocalStorage = () => {
  7. return hasWindow() && window.localStorage ? true : false;
  8. };
  9. const getItem = (key, ttl = 0) => {
  10. if (!hasLocalStorage()) return null;
  11. let value = window.localStorage.getItem(key);
  12. if (!value) return null;
  13. const now = new Date()?.getTime();
  14. const payload = JSON.parse(value);
  15. if (payload?.expiry > 0 && now > payload?.expiry) {
  16. removeItem(key);
  17. return null;
  18. }
  19. return payload?.value || null;
  20. };
  21. const setItem = (key, value, ttl = 0) => {
  22. if (!hasLocalStorage()) return false;
  23. const now = new Date()?.getTime();
  24. const payload = {
  25. value: value,
  26. expiry: (ttl && now + ttl) || 0
  27. };
  28. return window.localStorage.setItem(key, JSON.stringify(payload));
  29. };
  30. const removeItem = (key) => {
  31. if (!hasLocalStorage()) return null;
  32. return window.localStorage.removeItem(key);
  33. };
  34. // use local storage to store state values for persistent values
  35. export function useLocalStorage(key, initialValue, ttl = 0) {
  36. const [storedValue, setStoredValue] = useState(() => {
  37. try {
  38. const item = getItem(key, ttl);
  39. return (item && JSON.parse(item)) || initialValue;
  40. } catch (err) {
  41. log.error("useLocalStorage useState error", err.message || err);
  42. return initialValue;
  43. }
  44. });
  45. const setValue = (value) => {
  46. try {
  47. const item = isFunction(value) ? value(storedValue) : value;
  48. setStoredValue(item);
  49. setItem(key, stringify(item), ttl);
  50. } catch (err) {
  51. log.error("useLocalStorage setValue error", err.message || err);
  52. }
  53. };
  54. return [storedValue, setValue];
  55. }
  56. export function useStateCallback(initialState) {
  57. const [state, setState] = useState(initialState);
  58. const cbRef = useRef(null); // mutable ref to store current callback
  59. const setStateCallback = (state, cb) => {
  60. cbRef.current = cb; // store passed callback to ref
  61. setState(state);
  62. };
  63. useEffect(() => {
  64. // cb.current is `null` on initial render, so we only execute cb on state *updates*
  65. if (cbRef.current) {
  66. cbRef.current(state);
  67. cbRef.current = null; // reset callback after execution
  68. }
  69. }, [state]);
  70. return [state, setStateCallback];
  71. }