如何在React中管理indexeddb

问题描述 投票:0回答:2

我正在尝试在React组件中使用indexeddb(通过idb作为承诺包装器)。我正在管理与

useEffect
的联系,但我一直在为此苦苦挣扎:

const Component = () => {
  const [database, setDatabase] = useState();
  useEffect(() => {
    /* to use async in `useEfect` I have to move it to its own fn */
    const getDatabase = async () => {
      const db = await openDB("my-db", 1, {
        upgrade(db) {
          db.createObjectStore("my-store");
        }
      });
      setDatabase(db);
    };

    getDatabase();
    return () => database.close();
  }, []);
  return null;
};

但是如果成分数量和

database.close()

get 被调用,

database
状态未定义并且会中断: Codesandbox 示例。 显然这是设计的

我发现了一个丑陋的解决方法,我使用

_database
来存储最新的数据库。

const Component = () => {
  const [database, setDatabase] = useState();
  useEffect(() => {
    /* to use async in `useEfect` I have to move it to its own fn */
    let _database
    const getDatabase = async () => {
      const db = await openDB("my-db", 1, {
        upgrade(db) {
          db.createObjectStore("my-store");
        }
      });
      setDatabase(db);
      _database = db
    };

    getDatabase();
    return () => _database.close();
  }, []);
  return null;
};

但这似乎不对。这样做的正确方法是什么?

reactjs indexeddb
2个回答
1
投票

我认为,你的解决方法并不是世界上最糟糕的事情。但我也认为,如果在调用

openDB
之后但在承诺解决之前卸载组件,仍然存在竞争条件,在这种情况下
_database
将在清理函数中未定义。我认为你可以做这样的事情来处理这种情况:

const Component = () => {
  const [database, setDatabase] = useState();
  useEffect(() => {
    let _database, closed;
    const getDatabase = async () => {
      const db = await openDB("my-db", 1, {
        upgrade(db) {
          db.createObjectStore("my-store");
        }
      });
      if (closed) {
        db.close();
      } else {
        setDatabase(db);
        _database = db
      }
    };

    getDatabase();
    return () => {
      if (_database) {
        _database.close();
      }
      closed = true;
    }
  }, []);
  return null;
};

我同意这不是世界上最好看的代码。所以大部分时间它都与 IndexedDB 配合使用。

就个人而言,我在使用 React 和 IndexedDB 时所做的就是在调用

ReactDOM.render
之前连接到数据库,并将该连接存储在 React 之外。然后我就可以随心所欲地使用它,而不必担心它与 React 组件生命周期的关系。不过,我认为将其放入某些包装器 React 组件中本质上并没有什么问题。你只需要小心。但是嘿,这是 IndexedDB,无论如何你都要小心:)


0
投票
const useIndexedDB = (dbName: string, storeName: string, keyPath: string) => {
  const [db, setDb] = useState<IDBDatabase | null>(null);

  useEffect(() => {
    if (window === undefined) {
      return;
    }
    const openDb = async () => {
      const request = window.indexedDB.open(dbName, 1);
      request.onupgradeneeded = (event) => {
        if (!event.target) {
          return;
        }
        // @ts-ignore
        const db = event.target.result as IDBDatabase;
        db.createObjectStore(storeName, { keyPath });
      };
      request.onsuccess = (event) => {
        if (!event.target) {
          return;
        }
        // @ts-ignore
        const db = event.target.result as IDBDatabase;
        setDb(db);
      };
    };
    openDb();
  }, []);

  const add = async (data: any) => {
    if (!db) {
      return;
    }
    const transaction = db.transaction(storeName, "readwrite");
    const store = transaction.objectStore(storeName);
    store.add(data);
    return transaction.oncomplete;
  };

  const get = async (key: string) => {
    if (!db) {
      return;
    }
    const transaction = db.transaction(storeName, "readonly");
    const store = transaction.objectStore(storeName);
    return store.get(key);
  };

  const getAll = async () => {
    if (!db) {
      return;
    }
    const transaction = db.transaction(storeName, "readonly");
    const store = transaction.objectStore(storeName);
    return store.getAll();
  };

  const deleteItem = async (key: string) => {
    if (!db) {
      return;
    }
    const transaction = db.transaction(storeName, "readwrite");
    const store = transaction.objectStore(storeName);
    store.delete(key);
    return transaction.oncomplete;
  };

  return { add, get, getAll, deleteItem };
};

看起来您已经创建了一个名为 useIndexedDB 的自定义 React hook,它提供了与基于浏览器的数据库 IndexedDB 交互的基本功能。该钩子初始化和管理 IndexedDB 连接并公开函数来执行常见操作,例如添加数据、按键检索数据、检索所有数据和删除项目。

 const [inputData, setInputData] = useState('');
  const { add, get, getAll, deleteItem } = useIndexedDB('myDatabase', 'myStore', 'id');

  const handleAdd = async () => {
    if (inputData.trim() === '') return;

    const data = {
      id: Date.now().toString(),
      value: inputData.trim(),
    };

    await add(data);
    setInputData('');
  };

  const handleGet = async () => {
    const key = prompt('Enter the key to retrieve data:');
    if (!key) return;

    const result = await get(key);
    alert(result ? `Value for key ${key}: ${result.value}` : 'Key not found');
  };

  const handleGetAll = async () => {
    const result = await getAll();
    alert(result ? `All data: ${JSON.stringify(result)}` : 'No data found');
  };

  const handleDelete = async () => {
    const key = prompt('Enter the key to delete:');
    if (!key) return;

    await deleteItem(key);
    alert(`Item with key ${key} deleted`);
  };

  return (
    <div>
      <input
        type="text"
        value={inputData}
        onChange={(e) => setInputData(e.target.value)}
        placeholder="Enter data"
      />
      <button onClick={handleAdd}>Add Data</button>
      <button onClick={handleGet}>Get Data</button>
      <button onClick={handleGetAll}>Get All Data</button>
      <button onClick={handleDelete}>Delete Data</button>
    </div>
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.