use-persisted-state.ts 1.4 KB

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