You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 15:35:47