Использование крючка LocalStorage в React с помощью TypeScript

🚨 Смотрите на YouTube

У меня есть приложение для повышения производительности, и обычно я храню вещи, которые могут работать без бэкенда, в локальном хранилище. Здесь у нас есть хук, который обеспечивает состояние для виджета, воспроизводящего звуки фокуса. Он хранит все в локальном хранилище, и я называю его usePersistentStorage. Он получает ключ для локального хранилища и необязательное начальное значение.

const focusSoundsInitialState: FocusSoundsState = {
  isEnabled: false,
  mode: "compilation",
  compilationConfiguration: defaultFocusSoundsComilationConfiguration,
  configuration: {
    fire: 0.8,
    night: 0.2,
    seaside: 0.2,
  },
}

// inside of the component
const [state, setState] = usePersistentStorageValue(
  "focus-sounds",
  focusSoundsInitialState
)
Вход в полноэкранный режим Выход из полноэкранного режима

В хуке у нас есть useState с функцией инициализатора и useEffect, который слушает изменение состояния и обновляет localStorage. В инициализаторе мы начинаем с получения значения из локального хранилища. Затем мы проверяем, является ли оно объектом, и возвращаем либо значение из хранилища, либо начальное значение.

import { useEffect, useState } from "react"

import { persistentStorage } from "./persistentStorage"

export function usePersistentStorageValue<T>(key: string, initialValue?: T) {
  const [value, setValue] = useState<T>(() => {
    const valueFromStorage = persistentStorage.getItem(key)

    if (
      typeof initialValue === "object" &&
      !Array.isArray(initialValue) &&
      initialValue !== null
    ) {
      return {
        ...initialValue,
        ...valueFromStorage,
      }
    }

    return valueFromStorage || initialValue
  })

  useEffect(() => {
    persistentStorage.setItem(key, value)
  }, [key, value])

  return [value, setValue] as const
}
Вход в полноэкранный режим Выход из полноэкранного режима

Может возникнуть ситуация, когда мы захотим использовать что-то отличное от localStorage, поэтому у нас есть абстракция постоянного хранилища. У нее есть два метода, один для получения значения, а другой для установки.

interface PersistentStorage {
  getItem(key: string): string | null
  setItem(key: string, value: any): void
}

class LocalStorage implements PersistentStorage {
  getItem(key: string) {
    const item = localStorage.getItem(key)

    if (item === null) return undefined

    if (item === "null") return null
    if (item === "undefined") return undefined

    try {
      return JSON.parse(item)
    } catch {}

    return item
  }
  setItem(key: string, value: any) {
    if (value === undefined) {
      localStorage.removeItem(key)
    } else {
      localStorage.setItem(key, JSON.stringify(value))
    }
  }
}

class MockStorage implements PersistentStorage {
  getItem() {
    return null
  }
  setItem() {}
}

export const persistentStorage = window?.localStorage
  ? new LocalStorage()
  : new MockStorage()
Вход в полноэкранный режим Выход из полноэкранного режима

Если в окне нет локального хранилища, мы можем предоставить запасной вариант, но я об этом не беспокоюсь. В getItem у нас есть причудливые проверки. Иногда нам нужно отличить null от undefined. В конце концов, мы возвращаем разобранный результат. С форматом может быть что-то не так, поэтому мы оборачиваем его try-catch. Если мы хотим изменить формат хранимого значения, мы можем перейти к нему, изменив ключ. Один из подходов заключается в обновлении постфикса даты ключа каждый раз, когда мы хотим выполнить миграцию.

Оцените статью
Procodings.ru
Добавить комментарий