use-persisted-state.ts 1.4 KB

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