use-persisted-state.js 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. import { useState, useCallback, useEffect } from 'react'
  2. import localStorage from '../../infrastructure/local-storage'
  3. /**
  4. * @param {string} key
  5. * @param {any} [defaultValue]
  6. * @param {boolean} [listen]
  7. *
  8. * @returns {[any, function]}
  9. */
  10. function usePersistedState(key, defaultValue, listen = false) {
  11. const [value, setValue] = useState(() => {
  12. return localStorage.getItem(key) ?? defaultValue
  13. })
  14. const updateFunction = useCallback(
  15. newValue => {
  16. setValue(value => {
  17. const actualNewValue =
  18. typeof newValue === 'function' ? newValue(value) : newValue
  19. if (actualNewValue === defaultValue) {
  20. localStorage.removeItem(key)
  21. } else {
  22. localStorage.setItem(key, actualNewValue)
  23. }
  24. return actualNewValue
  25. })
  26. },
  27. [key, defaultValue]
  28. )
  29. useEffect(() => {
  30. if (listen) {
  31. const listener = event => {
  32. if (event.key === key) {
  33. // note: this value is read via getItem rather than from event.newValue
  34. // because getItem handles deserializing the JSON that's stored in localStorage.
  35. setValue(localStorage.getItem(key) ?? defaultValue)
  36. }
  37. }
  38. window.addEventListener('storage', listener)
  39. return () => {
  40. window.removeEventListener('storage', listener)
  41. }
  42. }
  43. }, [key, listen, defaultValue])
  44. return [value, updateFunction]
  45. }
  46. export default usePersistedState