用途
本技能为 TanStack DB 提供离线事务支持。它建立在 db-core 与 mutations-optimistic 之上,用于让乐观变更在页面刷新、断网或标签页切换后依然保留,并在恢复连接后可靠地提交到服务端。
主要能力
- 持久化发件箱:事务被序列化并写入存储,默认使用 IndexedDB,回退到 localStorage,从而在页面重载后仍可重放。
- 领导者选举:通过 WebLocks(回退 BroadcastChannel)确保只有一个标签页处理发件箱,避免重复执行。非领导者标签页回退到常规的 createTransaction / createOptimisticAction(仅在线、无持久化)。
- 在线检测:基于 navigator.onLine 与事件,在连接状态变化时暂停或恢复处理。
- 重试与退避:TransactionExecutor 负责执行,失败时按指数退避重试,可配置并发上限与抖动。
- 键调度:KeyScheduler 将触及相同键的事务串行化。
- 存储降级:启动时探测存储可用性,不可用时进入 online-only 模式,变更仍可正常工作但不会跨页面重载持久化。
- 幂等键:每个离线事务都带有 idempotencyKey,可传给 API 以防止重试导致重复执行。
- 错误分类:抛出 NonRetriableError 可停止重试并将事务移出发件箱,适用于永久性失败(校验错误、冲突、4xx 响应)。
- React Native 适配:从 @tanstack/offline-transactions/react-native 导入,自动使用 ReactNativeOnlineDetector 与 AsyncStorage 兼容存储。
使用方式
基本设置:从 @tanstack/offline-transactions 导入 startOfflineExecutor 与 IndexedDBAdapter,传入 collections 与 mutationFns,然后调用 await executor.waitForInit() 等待初始化(存储探测、领导者选举、发件箱重放)完成。
创建离线事务:使用 executor.createOfflineTransaction({ mutationFnName }) 创建事务,在 tx.mutate() 内执行变更(使用环境事务上下文),然后 tx.commit()。若执行器不是领导者标签页,则直接回退到 createTransaction(无离线持久化)。
创建离线动作:使用 executor.createOfflineAction({ mutationFnName, onMutate }) 定义动作,调用时传入变量。非领导者标签页回退到 createOptimisticAction。
事务生命周期:变更先乐观应用到集合(即时 UI 更新)→ 事务序列化并持久化到发件箱 → 领导者标签页取出并执行 mutationFn → 成功则移出发件箱并解决乐观状态;失败则按指数退避重试;页面重载则重放发件箱并恢复乐观状态。
配置项包括 collections、mutationFns、storage、maxConcurrency、jitter、beforeRetry、onUnknownMutationFn、onLeadershipChange、onStorageFailure、leaderElection、onlineDetector。可自定义 StorageAdapter(get/set/delete/keys/clear)。
发件箱管理:peekOutbox() 查看待处理事务,getPendingCount() 与 getRunningCount() 获取计数,clearOutbox() 清空,dispose() 清理。
适用场景
适用于需要在弱网、断网或页面刷新后仍保持乐观变更并最终提交的 TanStack DB 应用,尤其是多标签页场景下需要避免重复执行、以及需要幂等重试保障的服务端交互。
限制与注意事项
- 必须先阅读 db-core 与 mutations-optimistic 技能。
- 必须等待 waitForInit() 完成后再创建事务,否则可能错过领导者选举结果并使用错误代码路径。
- 必须将 idempotencyKey 传给 API,否则重试可能在服务端产生重复记录。
- collections 映射用于从发件箱恢复乐观状态,缺失会导致重放时无法显示待处理变更的乐观状态。
- 应区分可重试与永久错误,对 4xx 响应抛出 NonRetriableError,否则会一直重试直到上限。
- 存储不可用(隐私浏览、配额超限)时进入 online-only 模式,变更不跨页面重载持久化。
- 非领导者标签页不具备离线持久化能力。