最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React Hooks項(xiàng)目中使用IDB 8.x的實(shí)現(xiàn)

 更新時間:2025年09月03日 09:05:37   作者:藍(lán)銀草同學(xué)  
IDB8.x是IndexedDB的輕量級封裝庫,提供基于Promise的簡潔API,結(jié)合React Hooks可實(shí)現(xiàn)高效、可靠的客戶端數(shù)據(jù)存儲,本文就來介紹一下React Hooks項(xiàng)目中使用IDB 8.x的實(shí)現(xiàn),感興趣的可以了解一下

什么是 IDB?

IDB 是一個輕量級的 IndexedDB 包裝庫,它提供了基于 Promise 的 API,讓 IndexedDB 的使用變得更加簡單直觀。相比于原生 IndexedDB 復(fù)雜的回調(diào)機(jī)制,IDB 提供了更現(xiàn)代化、更易用的接口。8.x 版本帶來了更好的性能和更簡潔的 API。

IDB 8.x 核心 API

1. 打開/創(chuàng)建數(shù)據(jù)庫

import { openDB } from 'idb';

// 打開或創(chuàng)建數(shù)據(jù)庫 - 推薦使用版本管理
const openDatabase = async () => {
  return openDB('my-database', 2, {
    upgrade(db, oldVersion) {
      console.log(`Upgrading database from version ${oldVersion} to 2`);
      
      // 版本遷移邏輯
      if (oldVersion < 1) {
        // 版本 0 到 1 的遷移
        const store = db.createObjectStore('store1', { 
          keyPath: 'id',
          autoIncrement: true 
        });
        store.createIndex('name-index', 'name');
        store.createIndex('age-index', 'age');
      }
      
      if (oldVersion < 2) {
        // 版本 1 到 2 的遷移
        const newStore = db.createObjectStore('store2', {
          keyPath: 'uuid'
        });
        newStore.createIndex('category-index', 'category');
      }
    },
    
    // 數(shù)據(jù)庫被其他標(biāo)簽頁阻塞時的處理
    blocked(currentVersion, blockedVersion) {
      console.warn(`Database is blocked by version ${blockedVersion}`);
      // 可以提示用戶關(guān)閉其他標(biāo)簽頁
    },
    
    // 數(shù)據(jù)庫連接終止時的處理
    terminating() {
      console.warn('Database connection is terminating');
    },
    
    // 數(shù)據(jù)庫關(guān)閉時的處理
    closed() {
      console.log('Database connection closed');
    }
  });
};

// 使用示例
const db = await openDatabase();

2. 基本 CRUD 操作

// 添加數(shù)據(jù) - 返回生成的ID
const addData = async () => {
  const id = await db.add('store1', { 
    name: 'Alice', 
    age: 30,
    createdAt: new Date()
  });
  return id;
};

// 讀取數(shù)據(jù)
const getData = async (id) => {
  const data = await db.get('store1', id);
  return data;
};

// 更新數(shù)據(jù) - put 方法會創(chuàng)建或更新
const updateData = async (id, updates) => {
  const existing = await db.get('store1', id);
  await db.put('store1', { 
    ...existing, 
    ...updates,
    updatedAt: new Date()
  });
};

// 刪除數(shù)據(jù)
const deleteData = async (id) => {
  await db.delete('store1', id);
};

// 計數(shù) - 獲取存儲中的對象數(shù)量
const countData = async () => {
  const count = await db.count('store1');
  return count;
};

3. 事務(wù)操作

// 使用事務(wù)進(jìn)行多個操作
const performTransaction = async () => {
  const tx = db.transaction('store1', 'readwrite');
  const store = tx.objectStore('store1');

  try {
    await store.add({ name: 'Bob', age: 25, createdAt: new Date() });
    await store.add({ name: 'Charlie', age: 35, createdAt: new Date() });
    await tx.done; // 確保事務(wù)完成
    console.log('Transaction completed successfully');
  } catch (error) {
    console.error('Transaction failed:', error);
    tx.abort(); // 顯式中止事務(wù)
    throw error;
  }
};

// 使用事務(wù)獲取多個對象
const getMultipleItems = async (keys) => {
  const values = await db.getAll('store1', keys);
  return values;
};

4. 高級查詢操作

// 使用索引范圍查詢
const queryByAge = async (minAge) => {
  const index = db.transaction('store1').store.index('age-index');
  const adults = await index.getAll(IDBKeyRange.lowerBound(minAge));
  return adults;
};

// 使用游標(biāo)遍歷 - 更高效的方式
const iterateWithCursor = async (callback) => {
  const tx = db.transaction('store1', 'readonly');
  const store = tx.objectStore('store1');
  let cursor = await store.openCursor();
  
  while (cursor) {
    await callback(cursor.value);
    cursor = await cursor.continue();
  }
};

// 使用鍵范圍進(jìn)行復(fù)雜查詢
const complexQuery = async () => {
  const range = IDBKeyRange.bound(18, 65); // 年齡在18-65之間
  const results = await db.getAll('store1', range);
  return results;
};

在 React Hooks 項(xiàng)目中使用 IDB 8.x

1. 安裝依賴

npm install idb

2. 創(chuàng)建數(shù)據(jù)庫配置

// lib/db.js
import { openDB } from 'idb';

// 數(shù)據(jù)庫常量
export const DB_NAME = 'myAppDB';
export const DB_VERSION = 3;
export const STORE_NAMES = {
  TODOS: 'todos',
  NOTES: 'notes',
  SETTINGS: 'settings'
};

// 數(shù)據(jù)庫服務(wù)類
class DatabaseService {
  constructor() {
    this.db = null;
    this.isInitializing = false;
  }

  // 初始化數(shù)據(jù)庫
  async init() {
    if (this.db) return this.db;
    if (this.isInitializing) {
      // 如果已經(jīng)在初始化,等待初始化完成
      return new Promise((resolve) => {
        const checkInit = () => {
          if (this.db) {
            resolve(this.db);
          } else {
            setTimeout(checkInit, 100);
          }
        };
        checkInit();
      });
    }

    this.isInitializing = true;
    
    try {
      this.db = await openDB(DB_NAME, DB_VERSION, {
        upgrade: (db, oldVersion) => {
          console.log(`Database upgrade from version ${oldVersion} to ${DB_VERSION}`);

          // 版本遷移策略
          if (oldVersion < 1) {
            // 創(chuàng)建 todos 存儲
            const todoStore = db.createObjectStore(STORE_NAMES.TODOS, {
              keyPath: 'id',
              autoIncrement: true,
            });
            todoStore.createIndex('completed-index', 'completed');
            todoStore.createIndex('createdAt-index', 'createdAt');
          }

          if (oldVersion < 2) {
            // 創(chuàng)建 notes 存儲
            const noteStore = db.createObjectStore(STORE_NAMES.NOTES, {
              keyPath: 'id',
              autoIncrement: true,
            });
            noteStore.createIndex('title-index', 'title');
          }

          if (oldVersion < 3) {
            // 創(chuàng)建 settings 存儲
            db.createObjectStore(STORE_NAMES.SETTINGS, {
              keyPath: 'key',
            });
          }
        },
        blocked: () => {
          console.warn('Database is blocked by another tab');
        },
        terminating: () => {
          console.warn('Database connection is terminating');
        },
      });

      return this.db;
    } catch (error) {
      console.error('Database initialization failed:', error);
      throw error;
    } finally {
      this.isInitializing = false;
    }
  }

  // 獲取數(shù)據(jù)庫實(shí)例
  async getDB() {
    if (!this.db) {
      return await this.init();
    }
    return this.db;
  }

  // 關(guān)閉數(shù)據(jù)庫連接
  close() {
    if (this.db) {
      this.db.close();
      this.db = null;
    }
  }

  // 檢查數(shù)據(jù)庫是否已打開
  isOpen() {
    return !!this.db;
  }

  // 健康檢查
  async healthCheck() {
    try {
      const db = await this.getDB();
      const testKey = await db.add(STORE_NAMES.TODOS, {
        text: 'Health check',
        completed: false,
        createdAt: new Date(),
        updatedAt: new Date(),
      });
      
      await db.get(STORE_NAMES.TODOS, testKey);
      await db.delete(STORE_NAMES.TODOS, testKey);
      
      return { status: 'healthy', message: 'Database is functioning properly' };
    } catch (error) {
      console.error('Database health check failed:', error);
      
      try {
        // 嘗試重新初始化
        this.close();
        await this.init();
        
        return { status: 'degraded', message: 'Database recovered after reinitialization' };
      } catch (reinitError) {
        return { status: 'unhealthy', message: `Database is unavailable: ${reinitError.message}` };
      }
    }
  }
}

// 創(chuàng)建單例實(shí)例
export const dbService = new DatabaseService();

3. 創(chuàng)建自定義 Hook

// hooks/useIndexedDB.js
import { useState, useEffect, useCallback, useRef } from 'react';
import { dbService, STORE_NAMES } from '../lib/db';

export const useIndexedDB = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);
  const isMounted = useRef(true);

  // 清理函數(shù)
  useEffect(() => {
    return () => {
      isMounted.current = false;
    };
  }, []);

  // 添加項(xiàng)目
  const addItem = useCallback(async (storeName, item) => {
    try {
      const db = await dbService.getDB();
      const id = await db.add(storeName, {
        ...item,
        createdAt: new Date(),
        updatedAt: new Date(),
      });
      return id;
    } catch (err) {
      const error = err instanceof Error ? err : new Error('Unknown error occurred');
      if (isMounted.current) {
        setError(error);
      }
      throw error;
    }
  }, []);

  // 獲取項(xiàng)目
  const getItem = useCallback(async (storeName, key) => {
    try {
      const db = await dbService.getDB();
      return await db.get(storeName, key);
    } catch (err) {
      const error = err instanceof Error ? err : new Error('Unknown error occurred');
      if (isMounted.current) {
        setError(error);
      }
      throw error;
    }
  }, []);

  // 獲取所有項(xiàng)目
  const getAllItems = useCallback(async (storeName, indexName, query) => {
    try {
      const db = await dbService.getDB();
      
      if (indexName && query !== undefined) {
        const tx = db.transaction(storeName, 'readonly');
        const store = tx.objectStore(storeName);
        const index = store.index(indexName);
        return await index.getAll(query);
      }
      
      return await db.getAll(storeName);
    } catch (err) {
      const error = err instanceof Error ? err : new Error('Unknown error occurred');
      if (isMounted.current) {
        setError(error);
      }
      throw error;
    }
  }, []);

  // 更新項(xiàng)目
  const updateItem = useCallback(async (storeName, item) => {
    try {
      const db = await dbService.getDB();
      await db.put(storeName, {
        ...item,
        updatedAt: new Date(),
      });
    } catch (err) {
      const error = err instanceof Error ? err : new Error('Unknown error occurred');
      if (isMounted.current) {
        setError(error);
      }
      throw error;
    }
  }, []);

  // 刪除項(xiàng)目
  const deleteItem = useCallback(async (storeName, key) => {
    try {
      const db = await dbService.getDB();
      await db.delete(storeName, key);
    } catch (err) {
      const error = err instanceof Error ? err : new Error('Unknown error occurred');
      if (isMounted.current) {
        setError(error);
      }
      throw error;
    }
  }, []);

  // 清空存儲
  const clearStore = useCallback(async (storeName) => {
    try {
      const db = await dbService.getDB();
      await db.clear(storeName);
    } catch (err) {
      const error = err instanceof Error ? err : new Error('Unknown error occurred');
      if (isMounted.current) {
        setError(error);
      }
      throw error;
    }
  }, []);

  // 初始化數(shù)據(jù)庫
  useEffect(() => {
    const initializeDB = async () => {
      try {
        setIsLoading(true);
        await dbService.init();
        if (isMounted.current) {
          setError(null);
        }
      } catch (err) {
        const error = err instanceof Error ? err : new Error('Failed to initialize database');
        if (isMounted.current) {
          setError(error);
        }
      } finally {
        if (isMounted.current) {
          setIsLoading(false);
        }
      }
    };

    initializeDB();

    // 清理函數(shù)
    return () => {
      dbService.close();
    };
  }, []);

  return {
    isLoading,
    error,
    addItem,
    getItem,
    getAllItems,
    updateItem,
    deleteItem,
    clearStore,
  };
};

4. 創(chuàng)建針對待辦事項(xiàng)的專用 Hook

// hooks/useTodos.js
import { useCallback } from 'react';
import { useIndexedDB } from './useIndexedDB';
import { STORE_NAMES } from '../lib/db';

export const useTodos = () => {
  const { 
    isLoading, 
    error, 
    addItem, 
    getAllItems, 
    updateItem, 
    deleteItem 
  } = useIndexedDB();

  // 獲取待辦事項(xiàng)
  const getTodos = useCallback(async (completed) => {
    try {
      if (completed !== undefined) {
        return await getAllItems(STORE_NAMES.TODOS, 'completed-index', completed);
      }
      return await getAllItems(STORE_NAMES.TODOS);
    } catch (error) {
      console.error('Failed to get todos:', error);
      throw error;
    }
  }, [getAllItems]);

  // 添加待辦事項(xiàng)
  const addTodo = useCallback(async (text) => {
    if (!text || !text.trim()) {
      throw new Error('Todo text cannot be empty');
    }
    
    return await addItem(STORE_NAMES.TODOS, {
      text: text.trim(),
      completed: false,
    });
  }, [addItem]);

  // 切換待辦事項(xiàng)狀態(tài)
  const toggleTodo = useCallback(async (id) => {
    try {
      const todos = await getTodos();
      const todo = todos.find(t => t.id === id);
      
      if (!todo) {
        throw new Error('Todo not found');
      }
      
      await updateItem(STORE_NAMES.TODOS, {
        ...todo,
        completed: !todo.completed,
      });
    } catch (error) {
      console.error('Failed to toggle todo:', error);
      throw error;
    }
  }, [getTodos, updateItem]);

  // 刪除待辦事項(xiàng)
  const removeTodo = useCallback(async (id) => {
    await deleteItem(STORE_NAMES.TODOS, id);
  }, [deleteItem]);

  // 更新待辦事項(xiàng)文本
  const updateTodoText = useCallback(async (id, text) => {
    if (!text || !text.trim()) {
      throw new Error('Todo text cannot be empty');
    }
    
    try {
      const todos = await getTodos();
      const todo = todos.find(t => t.id === id);
      
      if (!todo) {
        throw new Error('Todo not found');
      }
      
      await updateItem(STORE_NAMES.TODOS, {
        ...todo,
        text: text.trim(),
      });
    } catch (error) {
      console.error('Failed to update todo text:', error);
      throw error;
    }
  }, [getTodos, updateItem]);

  // 批量切換待辦事項(xiàng)狀態(tài)
  const toggleAllTodos = useCallback(async (completed) => {
    try {
      const todos = await getTodos();
      const txPromises = todos.map(todo => 
        updateItem(STORE_NAMES.TODOS, {
          ...todo,
          completed: completed,
        })
      );
      
      await Promise.all(txPromises);
    } catch (error) {
      console.error('Failed to toggle all todos:', error);
      throw error;
    }
  }, [getTodos, updateItem]);

  // 清除已完成待辦事項(xiàng)
  const clearCompleted = useCallback(async () => {
    try {
      const completedTodos = await getTodos(true);
      const deletePromises = completedTodos.map(todo => 
        deleteItem(STORE_NAMES.TODOS, todo.id)
      );
      
      await Promise.all(deletePromises);
    } catch (error) {
      console.error('Failed to clear completed todos:', error);
      throw error;
    }
  }, [getTodos, deleteItem]);

  return {
    isLoading,
    error,
    getTodos,
    addTodo,
    toggleTodo,
    removeTodo,
    updateTodoText,
    toggleAllTodos,
    clearCompleted,
  };
};

5. 在組件中使用

// components/TodoList.js
import React, { useState, useEffect } from 'react';
import { useTodos } from '../hooks/useTodos';

const TodoList = () => {
  const [todos, setTodos] = useState([]);
  const [newTodo, setNewTodo] = useState('');
  const [filter, setFilter] = useState('all');
  const [isAdding, setIsAdding] = useState(false);
  const [editingId, setEditingId] = useState(null);
  const [editText, setEditText] = useState('');
  
  const { isLoading, error, getTodos, addTodo, toggleTodo, removeTodo, updateTodoText } = useTodos();

  // 加載待辦事項(xiàng)
  useEffect(() => {
    const loadTodos = async () => {
      if (!isLoading) {
        try {
          let todosData = [];
          
          switch (filter) {
            case 'active':
              todosData = await getTodos(false);
              break;
            case 'completed':
              todosData = await getTodos(true);
              break;
            default:
              todosData = await getTodos();
          }
          
          setTodos(todosData);
        } catch (err) {
          console.error('Failed to load todos:', err);
        }
      }
    };
    
    loadTodos();
  }, [isLoading, getTodos, filter]);

  // 添加新待辦事項(xiàng)
  const handleAddTodo = async () => {
    if (!newTodo.trim() || isAdding) return;
    
    setIsAdding(true);
    try {
      await addTodo(newTodo);
      setNewTodo('');
      
      // 重新加載待辦事項(xiàng)
      const todosData = await getTodos();
      setTodos(todosData);
    } catch (err) {
      console.error('Failed to add todo:', err);
      alert('Failed to add todo: ' + err.message);
    } finally {
      setIsAdding(false);
    }
  };

  // 切換待辦事項(xiàng)狀態(tài)
  const handleToggleTodo = async (id) => {
    try {
      await toggleTodo(id);
      // 重新加載待辦事項(xiàng)
      const todosData = await getTodos();
      setTodos(todosData);
    } catch (err) {
      console.error('Failed to toggle todo:', err);
      alert('Failed to toggle todo: ' + err.message);
    }
  };

  // 刪除待辦事項(xiàng)
  const handleDeleteTodo = async (id) => {
    if (!window.confirm('Are you sure you want to delete this todo?')) return;
    
    try {
      await removeTodo(id);
      // 重新加載待辦事項(xiàng)
      const todosData = await getTodos();
      setTodos(todosData);
    } catch (err) {
      console.error('Failed to delete todo:', err);
      alert('Failed to delete todo: ' + err.message);
    }
  };

  // 開始編輯
  const startEditing = (todo) => {
    setEditingId(todo.id);
    setEditText(todo.text);
  };

  // 取消編輯
  const cancelEditing = () => {
    setEditingId(null);
    setEditText('');
  };

  // 保存編輯
  const saveEdit = async (id) => {
    if (!editText.trim()) {
      alert('Todo text cannot be empty');
      return;
    }
    
    try {
      await updateTodoText(id, editText);
      setEditingId(null);
      setEditText('');
      
      // 重新加載待辦事項(xiàng)
      const todosData = await getTodos();
      setTodos(todosData);
    } catch (err) {
      console.error('Failed to update todo:', err);
      alert('Failed to update todo: ' + err.message);
    }
  };

  if (isLoading) {
    return (
      <div className="loading">
        <div className="spinner"></div>
        <p>Loading database...</p>
      </div>
    );
  }

  if (error) {
    return (
      <div className="error">
        <h2>Error Loading Todos</h2>
        <p>{error.message}</p>
        <button onClick={() => window.location.reload()}>Retry</button>
      </div>
    );
  }

  const activeCount = todos.filter(t => !t.completed).length;
  const completedCount = todos.length - activeCount;

  return (
    <div className="todo-container">
      <h1>Todo List with IndexedDB</h1>
      
      {/* 添加新待辦事項(xiàng) */}
      <div className="add-todo">
        <input
          type="text"
          value={newTodo}
          onChange={(e) => setNewTodo(e.target.value)}
          placeholder="What needs to be done?"
          onKeyPress={(e) => e.key === 'Enter' && handleAddTodo()}
          disabled={isAdding}
        />
        <button 
          onClick={handleAddTodo} 
          disabled={isAdding || !newTodo.trim()}
          className="add-btn"
        >
          {isAdding ? 'Adding...' : 'Add'}
        </button>
      </div>
      
      {/* 篩選選項(xiàng) */}
      <div className="filters">
        <button 
          className={filter === 'all' ? 'active' : ''} 
          onClick={() => setFilter('all')}
        >
          All ({todos.length})
        </button>
        <button 
          className={filter === 'active' ? 'active' : ''} 
          onClick={() => setFilter('active')}
        >
          Active ({activeCount})
        </button>
        <button 
          className={filter === 'completed' ? 'active' : ''} 
          onClick={() => setFilter('completed')}
        >
          Completed ({completedCount})
        </button>
      </div>
      
      {/* 待辦事項(xiàng)列表 */}
      {todos.length === 0 ? (
        <div className="empty-state">
          {filter === 'completed' 
            ? 'No completed todos' 
            : filter === 'active' 
            ? 'No active todos - great job!' 
            : 'No todos yet. Add one above!'
          }
        </div>
      ) : (
        <ul className="todo-list">
          {todos.map(todo => (
            <li key={todo.id} className={`todo-item ${todo.completed ? 'completed' : ''}`}>
              <input
                type="checkbox"
                checked={todo.completed}
                onChange={() => handleToggleTodo(todo.id)}
                className="todo-checkbox"
              />
              
              {editingId === todo.id ? (
                <div className="edit-mode">
                  <input
                    type="text"
                    value={editText}
                    onChange={(e) => setEditText(e.target.value)}
                    onKeyPress={(e) => e.key === 'Enter' && saveEdit(todo.id)}
                    className="edit-input"
                    autoFocus
                  />
                  <button onClick={() => saveEdit(todo.id)} className="save-btn">Save</button>
                  <button onClick={cancelEditing} className="cancel-btn">Cancel</button>
                </div>
              ) : (
                <div className="view-mode">
                  <span 
                    className="todo-text"
                    onDoubleClick={() => startEditing(todo)}
                  >
                    {todo.text}
                  </span>
                  <div className="todo-actions">
                    <button 
                      onClick={() => startEditing(todo)}
                      className="edit-btn"
                      title="Edit todo"
                    >
                      ??
                    </button>
                    <button 
                      onClick={() => handleDeleteTodo(todo.id)}
                      className="delete-btn"
                      title="Delete todo"
                    >
                      ???
                    </button>
                  </div>
                </div>
              )}
            </li>
          ))}
        </ul>
      )}
      
      {/* 統(tǒng)計信息 */}
      <div className="stats">
        <small>
          Total: {todos.length} | 
          Active: {activeCount} | 
          Completed: {completedCount}
        </small>
        <br />
        <small>
          Last updated: {todos.length > 0 
            ? new Date(Math.max(...todos.map(t => new Date(t.updatedAt).getTime()))).toLocaleString()
            : 'Never'
          }
        </small>
      </div>
    </div>
  );
};

export default TodoList;

最佳實(shí)踐和高級用法

1. 重試機(jī)制

// utils/retry.js
export const retryOperation = async (operation, maxRetries = 3, delay = 1000) => {
  let lastError;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await operation();
    } catch (error) {
      lastError = error;
      console.warn(`Operation failed (attempt ${i + 1}/${maxRetries}):`, error);
      
      if (i < maxRetries - 1) {
        // 指數(shù)退避策略
        const waitTime = delay * Math.pow(2, i);
        console.log(`Waiting ${waitTime}ms before retry...`);
        await new Promise(resolve => setTimeout(resolve, waitTime));
      }
    }
  }
  
  throw lastError;
};

// 在 Hook 中使用
const getItemWithRetry = async (storeName, key) => {
  return retryOperation(() => getItem(storeName, key));
};

2. 批量操作

// 批量添加項(xiàng)目
const addItemsInBatch = async (storeName, items, batchSize = 100) => {
  const db = await dbService.getDB();
  const tx = db.transaction(storeName, 'readwrite');
  const store = tx.objectStore(storeName);
  const results = [];
  
  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize);
    const batchPromises = batch.map(item => 
      store.add({
        ...item,
        createdAt: new Date(),
        updatedAt: new Date(),
      })
    );
    
    const batchResults = await Promise.all(batchPromises);
    results.push(...batchResults);
    
    // 讓出主線程,避免阻塞UI
    if (i + batchSize < items.length) {
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }
  
  await tx.done;
  return results;
};

// 批量刪除項(xiàng)目
const deleteItemsInBatch = async (storeName, keys, batchSize = 100) => {
  const db = await dbService.getDB();
  const tx = db.transaction(storeName, 'readwrite');
  const store = tx.objectStore(storeName);
  
  for (let i = 0; i < keys.length; i += batchSize) {
    const batch = keys.slice(i, i + batchSize);
    const batchPromises = batch.map(key => store.delete(key));
    
    await Promise.all(batchPromises);
    
    // 讓出主線程
    if (i + batchSize < keys.length) {
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }
  
  await tx.done;
};

3. 數(shù)據(jù)備份和恢復(fù)

// 導(dǎo)出數(shù)據(jù)
const exportData = async (storeName) => {
  const db = await dbService.getDB();
  const allData = await db.getAll(storeName);
  const blob = new Blob([JSON.stringify(allData, null, 2)], { 
    type: 'application/json' 
  });
  return blob;
};

// 導(dǎo)入數(shù)據(jù)
const importData = async (storeName, jsonData) => {
  const data = JSON.parse(jsonData);
  const db = await dbService.getDB();
  const tx = db.transaction(storeName, 'readwrite');
  const store = tx.objectStore(storeName);
  
  // 清空現(xiàn)有數(shù)據(jù)
  await store.clear();
  
  // 添加新數(shù)據(jù)
  for (const item of data) {
    await store.add({
      ...item,
      importedAt: new Date(),
    });
  }
  
  await tx.done;
  return data.length;
};

總結(jié)

IDB 8.x 提供了強(qiáng)大的 IndexedDB 操作能力,結(jié)合 React Hooks 可以創(chuàng)建高效、可靠的客戶端數(shù)據(jù)存儲解決方案。本文提供的代碼示例展示了:

  1. 健壯的數(shù)據(jù)庫配置:包含版本遷移、錯誤處理和單例模式
  2. 可重用的自定義 Hooks:封裝數(shù)據(jù)庫操作邏輯
  3. 完善的錯誤處理:包括重試機(jī)制和健康檢查
  4. 性能優(yōu)化:批量操作和事務(wù)管理
  5. 用戶體驗(yàn)優(yōu)化:加載狀態(tài)、編輯功能和確認(rèn)對話框

關(guān)鍵最佳實(shí)踐:

  • 使用明確的版本管理策略
  • 實(shí)現(xiàn)健壯的錯誤處理和重試機(jī)制
  • 使用事務(wù)確保數(shù)據(jù)一致性
  • 添加適當(dāng)?shù)募虞d狀態(tài)和用戶體驗(yàn)優(yōu)化
  • 定期進(jìn)行數(shù)據(jù)庫健康檢查
  • 實(shí)現(xiàn)數(shù)據(jù)備份和恢復(fù)功能

通過這些實(shí)踐,你可以在 React 應(yīng)用中構(gòu)建出生產(chǎn)級別的客戶端數(shù)據(jù)存儲解決方案,提供離線功能和更好的用戶體驗(yàn)。

到此這篇關(guān)于React Hooks項(xiàng)目中使用IDB 8.x的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)React Hooks使用IDB 8.x內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JS中使用react-tooltip插件實(shí)現(xiàn)鼠標(biāo)懸浮顯示框

    JS中使用react-tooltip插件實(shí)現(xiàn)鼠標(biāo)懸浮顯示框

    前段時間遇到的一個需求,要求鼠標(biāo)懸停顯示使用描述, 用到了react-tooltip插件,今天寫一個總結(jié),感興趣的朋友跟隨小編一起看看吧
    2019-05-05
  • React特征學(xué)習(xí)之Form格式示例詳解

    React特征學(xué)習(xí)之Form格式示例詳解

    這篇文章主要為大家介紹了React特征學(xué)習(xí)之Form格式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • React?Fiber構(gòu)建源碼解析

    React?Fiber構(gòu)建源碼解析

    這篇文章主要為大家介紹了React?Fiber構(gòu)建源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • react-pdf?打造在線簡歷生成器的示例代碼

    react-pdf?打造在線簡歷生成器的示例代碼

    本文主要介紹了react-pdf?打造在線簡歷生成器的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 實(shí)例講解React 組件

    實(shí)例講解React 組件

    這篇文章主要介紹了React 組件的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • react-redux及redux狀態(tài)管理工具使用詳解

    react-redux及redux狀態(tài)管理工具使用詳解

    Redux是為javascript應(yīng)用程序提供一個狀態(tài)管理工具集中的管理react中多個組件的狀態(tài)redux是專門作狀態(tài)管理的js庫(不是react插件庫可以用在其他js框架中例如vue,但是基本用在react中),這篇文章主要介紹了react-redux及redux狀態(tài)管理工具使用詳解,需要的朋友可以參考下
    2023-01-01
  • React庫之react-beautiful-dnd介紹及其使用過程

    React庫之react-beautiful-dnd介紹及其使用過程

    在使用React構(gòu)建Web應(yīng)用程序時,拖拽功能是一項(xiàng)常見需求,為了方便實(shí)現(xiàn)拖拽功能,我們可以借助第三方庫react-beautiful-dnd,本文將介紹react-beautiful-dnd的基本概念,并結(jié)合實(shí)際的項(xiàng)目代碼一步步詳細(xì)介紹其使用過程,需要的朋友可以參考下
    2023-11-11
  • React中的useEffect useLayoutEffect到底怎么用

    React中的useEffect useLayoutEffect到底怎么用

    這篇文章主要介紹了React中的useEffect useLayoutEffect具體使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-02-02
  • React超詳細(xì)講述Fiber的使用

    React超詳細(xì)講述Fiber的使用

    在fiber出現(xiàn)之前,react的架構(gòu)體系只有協(xié)調(diào)器reconciler和渲染器render。當(dāng)前有新的update時,react會遞歸所有的vdom節(jié)點(diǎn),如果dom節(jié)點(diǎn)過多,會導(dǎo)致其他事件影響滯后,造成卡頓。即之前的react版本無法中斷工作過程,一旦遞歸開始無法停留下來
    2023-02-02
  • react-router-dom入門使用教程(路由的模糊匹配與嚴(yán)格匹配)

    react-router-dom入門使用教程(路由的模糊匹配與嚴(yán)格匹配)

    這篇文章主要介紹了react-router-dom入門使用教程,主要介紹路由的模糊匹配與嚴(yán)格匹配,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08

最新評論

威远县| 临高县| 鹿泉市| 手机| 永福县| 炎陵县| 老河口市| 类乌齐县| 北碚区| 定安县| 界首市| 化德县| 边坝县| 凤庆县| 诸城市| 军事| 承德市| 腾冲县| 登封市| 沧州市| 会理县| 莲花县| 淳化县| 天全县| 清徐县| 荣昌县| 乐亭县| 偃师市| 和静县| 蚌埠市| 潮安县| 双江| 芜湖市| 泸西县| 体育| 韶关市| 和田市| 寿阳县| 南陵县| 嘉鱼县| 井研县|