Интеграция с React: хуки и управление состоянием

Для работы с IndexedDB через библиотеку Idb-keyval достаточно установить пакет через npm или yarn:

npm install idb-keyval

или

yarn add idb-keyval

После установки можно импортировать основные функции:

import { get, set, del, clear, keys } from 'idb-keyval';

Эти функции обеспечивают удобный асинхронный доступ к хранилищу без необходимости напрямую работать с низкоуровневым API IndexedDB.


Использование хуков для асинхронного состояния

В React часто возникает потребность хранить данные локально между сессиями. Idb-keyval можно использовать совместно с кастомными хуками для синхронизации состояния и IndexedDB.

Хук useIdbState

Пример создания кастомного хука useIdbState, который автоматически сохраняет и извлекает значение из IndexedDB:

import { useState, useEffect } from 'react';
import { get, set } from 'idb-keyval';

function useIdbState(key, initialValue) {
  const [state, setState] = useState(initialValue);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let mounted = true;
    get(key).then(value => {
      if (mounted) {
        if (value !== undefined) setState(value);
        setLoading(false);
      }
    });
    return () => { mounted = false; };
  }, [key]);

  const updateState = (newValue) => {
    setState(newValue);
    set(key, newValue);
  };

  return [state, updateState, loading];
}

Особенности:

  • loading позволяет контролировать состояние загрузки данных из IndexedDB.
  • Асинхронное чтение не блокирует рендер.
  • Любое изменение состояния автоматически сохраняется в IndexedDB.

Интеграция с формами

Хук useIdbState удобно использовать для сохранения состояния форм между сессиями:

function ProfileForm() {
  const [name, setName, loading] = useIdbState('profile-name', '');
  const [email, setEmail] = useIdbState('profile-email', '');

  if (loading) return <p>Загрузка...</p>;

  return (
    <form>
      <input
        type="text"
        value={name}
        onCha nge={(e) => setName(e.target.value)}
        placeholder="Имя"
      />
      <input
        type="email"
        value={email}
        onCha nge={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
    </form>
  );
}

Преимущества:

  • Автоматическое восстановление данных при перезагрузке страницы.
  • Минимальный код для синхронизации состояния с IndexedDB.

Работа с массивами и объектами

Idb-keyval поддерживает сохранение сложных объектов, массивов и других структур JavaScript. Для больших данных рекомендуется использовать методы get, set и keys:

import { get, set, keys } from 'idb-keyval';

async function addItemToList(item) {
  const list = (await get('todo-list')) || [];
  list.push(item);
  await set('todo-list', list);
}

async function getAllItems() {
  return await get('todo-list') || [];
}

Особенности работы с массивами:

  • Всегда нужно считывать текущие данные перед модификацией.
  • set перезаписывает значение полностью.
  • Использование ключей позволяет организовать несколько отдельных хранилищ.

Очистка и удаление данных

Для управления состоянием можно использовать del и clear:

import { del, clear } from 'idb-keyval';

// Удаление конкретного ключа
await del('profile-name');

// Очистка всего хранилища
await clear();

Это полезно для реализации функций выхода пользователя или сброса настроек.


Комбинирование с useEffect и контекстом

Для глобального состояния можно создать провайдер с контекстом:

import React, { createContext, useContext } from 'react';

const TodoContext = createContext();

export function TodoProvider({ children }) {
  const [todos, setTodos, loading] = useIdbState('todos', []);

  const addTodo = (text) => {
    const newTodos = [...todos, { text, id: Date.now() }];
    setTodos(newTodos);
  };

  return (
    <TodoContext.Provider value={{ todos, addTodo, loading }}>
      {children}
    </TodoContext.Provider>
  );
}

export const useTodos = () => useContext(TodoContext);

Преимущества такой архитектуры:

  • Центральное управление состоянием с автоматической синхронизацией в IndexedDB.
  • Легкая интеграция с компонентами React через useContext.
  • Минимизируется количество повторяющегося кода для работы с локальным хранилищем.

Оптимизация производительности

  • Делегирование больших объектов: сохранять только необходимые данные, чтобы избежать лишнего расхода памяти.
  • Ленивая загрузка: при необходимости подгружать данные по ключу только при первом использовании.
  • Обработка ошибок: Idb-keyval возвращает промисы, поэтому всегда полезно оборачивать вызовы в try/catch для безопасного сохранения:
try {
  await set('key', value);
} catch (error) {
  console.error('Ошибка при сохранении в IndexedDB', error);
}
  • Батчинг обновлений: при частых изменениях состояния можно использовать debounce или throttle для уменьшения количества операций записи.

Взаимодействие с серверным состоянием

Idb-keyval можно использовать для кэширования данных, полученных с сервера. Пример:

async function fetchAndCacheData(url, key) {
  const cached = await get(key);
  if (cached) return cached;

  const response = await fetch(url);
  const data = await response.json();
  await set(key, data);
  return data;
}

Преимущества:

  • Быстрый доступ к данным без лишних сетевых запросов.
  • Возможность работать оффлайн или при нестабильном соединении.
  • Автоматическое восстановление данных после перезагрузки страницы.

Итоговые рекомендации

  • Использовать кастомные хуки для интеграции с React.
  • Разделять данные по ключам, чтобы обеспечить модульность.
  • Применять контекст для глобального состояния, синхронизированного с IndexedDB.
  • Оборачивать асинхронные операции в try/catch.
  • Кэшировать данные сервера и обеспечивать оффлайн-доступ.

Такая организация работы с Idb-keyval позволяет создавать устойчивые, производительные приложения на React с минимальным количеством кода для работы с локальным хранилищем.