TanStack Query 缓存失效模式

TanStack Query 的四个缓存操作:cancelQueries、setQueryData、removeQueries、invalidateQueries 的使用场景和区别。

#type / howto #status / growing #tech / dev / frontend #resource / react

[!info] related notes

TanStack Query 缓存失效模式

核心问题

TanStack Query 提供了多个缓存操作 API,但它们的语义不同,用错会导致数据不一致或缓存泄漏。

四个 API 的分工

cancelQueries — 取消在途请求

await queryClient.cancelQueries({
  queryKey: ['consultation', 'session', id],
});

语义:停止正在飞行中的网络请求,防止旧请求后返回覆盖新数据。

使用场景

  • 乐观更新前(onMutate 里)
  • SSE 写 cache 前
  • 任何需要”先停旧的,再写新的”的场景

setQueryData — 直接写缓存

queryClient.setQueryData(
  ['consultation', 'conversations'],
  { conversations: [] },
);

语义:不发网络请求,直接用你提供的数据覆盖缓存。

使用场景

  • 删除后立即清空列表(乐观更新)
  • SSE 增量更新
  • 新建后预填缓存
  • mutation 拿到响应后直接更新

removeQueries — 删除缓存条目

queryClient.removeQueries({
  queryKey: ['consultation', 'conversation', deletedId],
});

语义:从缓存中彻底移除这个 queryKey 对应的数据。下次组件用到时会重新请求。

使用场景

  • 删除单个资源后,清除它的详情缓存
  • 删除全部资源后,清除所有详情缓存
  • 避免缓存泄漏(大量不再需要的缓存条目)

注意removeQueries 不会触发重新请求,只是删除缓存。如果组件还在用这个 queryKey,下次挂载时才会重新请求。

invalidateQueries — 标记缓存过期

queryClient.invalidateQueries({
  queryKey: ['consultation', 'session', id],
});

语义:做两件事:

  1. 把缓存标记为 stale(过期)
  2. 如果这个 queryKey 当前有活跃的 useQuery 订阅者 → 立即在后台 refetch
  3. 如果没有活跃订阅者 → 不发请求,下次有人用到时再请求

使用场景

  • mutation 成功后,让相关缓存自动刷新
  • SSE 结束后,做最终数据对齐
  • 用户手动触发刷新

决策指南:invalidate vs setQueryData

遇到”mutation 后如何更新缓存”时,按这个顺序判断:

① 已经知道完整的更新结果?
   → 是 → setQueryData(直接写缓存,零延迟)
   → 否 → 继续

② 不确定后端最终结果?
   → 是 → invalidateQueries(让后端决定最新数据)
   → 否 → 继续

③ 需要两者兼顾?
   → setQueryData 乐观更新(立即反映到 UI)
   → invalidateQueries 最终对齐(确保和后端一致)
场景推荐方式原因
重命名 titlesetQueryData已知新 title,不需要请求
置顶 pinnedsetQueryData已知新状态
删除会话setQueryData + removeQueries从列表移除 + 删除详情缓存
诊断分析invalidateQueries需要后端返回分析结果
生成治疗方案invalidateQueries需要后端返回完整方案
SSE titleGeneratedsetQueryData已知新 title
SSE extractedInfoUpdatesetQueryData增量更新,流结束后再 invalidate
创建新资源setQueryData(预填)或 invalidateQueries取决于创建接口是否返回完整数据

使用场景速查表

场景API说明
删除资源后清空详情缓存removeQueries彻底移除,避免缓存泄漏
删除资源后更新列表setQueryData立即从列表中移除
SSE 增量更新cancelQueries + setQueryData先取消旧请求再写入
mutation 成功后刷新invalidateQueries标记过期,自动重新请求
乐观更新前cancelQueries防止旧请求覆盖乐观数据
预填新建资源的缓存setQueryData避免 loading 闪烁

常见错误

removeQueries 误删目标

// ❌ predicate 可能误删
queryClient.removeQueries({
  queryKey: consultationKeys.all,
  predicate: (q) => q.queryKey.length > 1,
});
// consultationKeys.conversations() 的 key 长度也是 2,会被误删

// ✅ 精确删除
queryClient.removeQueries({
  queryKey: [...consultationKeys.all, 'conversation'],
});
queryClient.removeQueries({
  queryKey: [...consultationKeys.all, 'session'],
});

忘记 cancelQueries

// ❌ 直接写 cache,可能被旧请求覆盖
queryClient.setQueryData(key, newData);

// ✅ 先取消再写
await queryClient.cancelQueries({ queryKey: key });
queryClient.setQueryData(key, newData);

混淆 removeQueries 和 invalidateQueries

  • removeQueries:删缓存,不触发请求
  • invalidateQueries:标记过期,触发请求

删除资源用 removeQueries(不需要再请求已删除的数据)。修改资源用 invalidateQueries(需要获取最新数据)。

创建于 2026/7/2 更新于 2026/7/15