WebAssembly通过Rexie写IndexedDB时,如何让useLiveQuery正常更新?
问题解答
核心原因
useLiveQuery的更新逻辑完全依赖Dexie内部的数据库变更事件系统:当你通过Dexie实例执行增删改操作时,Dexie会自动触发对应的变更事件,useLiveQuery通过订阅这些事件来重新执行查询并更新组件。
而Rexie是独立于Dexie的IndexedDB操作库(哪怕底层都是操作同一个IndexedDB数据库),它的写入操作不会触发Dexie的内部事件——Dexie根本不知道数据已经被修改,所以useLiveQuery自然不会更新。
可行解决办法
1. 统一使用Dexie实例处理所有数据库操作
如果能调整Wasm层的实现,让Wasm通过调用JavaScript侧的Dexie实例来执行写入操作,而不是直接用Rexie。这样所有数据库操作都走Dexie的流程,useLiveQuery就能正常感知变化。
示例代码:
// JavaScript 侧 import Dexie from 'dexie'; import { useLiveQuery } from 'dexie-react-hooks'; // 初始化Dexie数据库 const db = new Dexie('MyDatabase'); db.version(1).stores({ items: 'id,content' }); // 将Dexie的table实例传递给Wasm wasmInstance.setDexieTable(db.items); // 组件中使用useLiveQuery function ItemList() { const items = useLiveQuery(() => db.items.toArray(), []); return ( <ul> {items?.map(item => ( <li key={item.id}>{item.content}</li> ))} </ul> ); }
// Wasm侧(以Rust + wasm-bindgen为例) use wasm_bindgen::JsValue; #[wasm_bindgen] pub struct WasmDB { table: JsValue, } #[wasm_bindgen] impl WasmDB { #[wasm_bindgen(setter)] pub fn set_dexie_table(&mut self, table: JsValue) { self.table = table; } pub async fn add_item(&self, id: u32, content: &str) -> Result<(), JsValue> { // 通过Dexie的table实例执行写入 let item = js_sys::Object::new(); js_sys::Reflect::set(&item, &JsValue::from_str("id"), &JsValue::from(id))?; js_sys::Reflect::set(&item, &JsValue::from_str("content"), &JsValue::from_str(content))?; let add_fn = js_sys::Reflect::get(&self.table, &JsValue::from_str("add"))?; let promise = js_sys::Function::unchecked_from_js_value(add_fn).call1(&self.table, &item)?; wasm_bindgen_futures::JsFuture::from(promise).await?; Ok(()) } }
2. 手动触发useLiveQuery刷新
如果无法替换Rexie的写入逻辑,可以在Wasm完成写入后,手动触发useLiveQuery重新执行查询。核心思路是维护一个状态变量,当写入完成后更新该变量,将其作为useLiveQuery的依赖项,强制查询刷新。
示例代码:
import { useState, useCallback } from 'react'; import Dexie from 'dexie'; import { useLiveQuery } from 'dexie-react-hooks'; const db = new Dexie('MyDatabase'); db.version(1).stores({ items: 'id,content' }); function ItemList() { // 用于触发刷新的状态变量 const [refreshCount, setRefreshCount] = useState(0); // 调用Wasm写入的函数,完成后更新状态 const writeViaWasm = useCallback(async () => { // 调用Wasm的写入方法 await wasmInstance.writeItemToDB(1, "新内容"); // 更新状态,强制useLiveQuery重新查询 setRefreshCount(prev => prev + 1); }, []); // 将refreshCount作为依赖项,状态变化时重新执行查询 const items = useLiveQuery(() => db.items.toArray(), [refreshCount]); return ( <div> <button onClick={writeViaWasm}>通过Wasm写入数据</button> <ul> {items?.map(item => ( <li key={item.id}>{item.content}</li> ))} </ul> </div> ); }
3. 监听IndexedDB全局变更事件(兜底方案)
如果以上两种方式都无法实现,可以直接监听IndexedDB的versionchange事件,当数据库发生变更时触发刷新。不过这种方式比较底层,可靠性不如前两种:
import { useState, useEffect } from 'react'; import Dexie from 'dexie'; import { useLiveQuery } from 'dexie-react-hooks'; const db = new Dexie('MyDatabase'); db.version(1).stores({ items: 'id,content' }); function ItemList() { const [refreshCount, setRefreshCount] = useState(0); useEffect(() => { const openReq = indexedDB.open('MyDatabase'); openReq.onsuccess = () => { const idb = openReq.result; // 监听版本变更事件 const handleChange = () => setRefreshCount(prev => prev + 1); idb.addEventListener('versionchange', handleChange); // 组件卸载时移除监听 return () => idb.removeEventListener('versionchange', handleChange); }; }, []); const items = useLiveQuery(() => db.items.toArray(), [refreshCount]); return ( <ul> {items?.map(item => ( <li key={item.id}>{item.content}</li> ))} </ul> ); }
内容的提问来源于stack exchange,提问作者a_b
相关产品推荐
相关产品推荐

