SuperAPI AI 工具集

tanstack-query

编程与开发

查看项目

更新于 10-15 08:17

技能介绍

用途

本技能用于排查和解决 TanStack Query / React Query 在生产环境中的问题,尤其是 v4 升级到 v5 的迁移错误、无限重取循环、SSR 水合(hydration)不匹配、缓存与失效策略等。它假设使用者已掌握 useQuery 基础,重点覆盖“会在生产环境出问题”的部分。

调用方式为 /tanstack-query,可附带参数描述具体的查询 bug、迁移问题或缓存决策,例如“dashboard 上的无限重取循环”“v4 到 v5 的 cacheTime 问题”。若参数为空,则先询问要分析哪个 TanStack Query 问题。

使用前的策略评估:何时不该用 React Query

资料强调一个常见误区:开发者把 React Query 当成万能状态管理器,但它本质是“服务端缓存”,不是状态管理器。以下场景不建议使用查询:

  • 数据来自 URL(搜索参数、路径):应使用框架的 loader(Next.js、Remix)。原因:查询按 key 缓存,而 URL 本身已经是缓存 key。
  • 派生/计算数据:应使用 useMemo 或 Zustand。原因:没有服务端、没有陈旧数据、无需重取。
  • 表单状态:应使用 React Hook Form 或受控状态。
  • WebSocket/实时数据(频率大于 1 次/秒):应使用 Zustand;查询是为请求/响应设计的,不适合流式数据。
  • REST/GraphQL 服务端状态:适合使用查询。

staleTime 选择建议

资料给出按更新频率选择 staleTime 的参考:

  • 实时(>1 次/秒):改用 WebSocket + Zustand。
  • 频繁(<1 次/分钟):30 秒–1 分钟。
  • 中等(5–30 分钟):5 分钟(默认值)。
  • 不频繁(>1 小时):30 分钟以上。
  • 关键数据(金额、认证):0(始终新鲜)。

v4 → v5 迁移陷阱

  1. cacheTime 改名为 gcTime。失败模式是静默的:代码能运行、TypeScript 不报错,但缓存会立即被垃圾回收。调试信号:DevTools 显示 gcTime 为 0ms,尽管设置了 10 分钟。

  2. isLoading 被移除,改用 isPending。失败模式:if (isLoading) 求值为假(undefined),加载动画永远不显示。语义差异:在有缓存数据时重取,isPending 仍为 true,而 isLoading 不会,因此简单替换会导致“陈旧数据与加载动画同时出现”。

  3. keepPreviousData 改为 placeholderData,且必须使用函数形式。失败模式:翻页时页面闪烁。

  4. 查询函数必须返回非 void。失败模式:使用 any 类型时出现静默运行时错误。

性能陷阱

  • 无限重取循环:原因是 queryKey 中包含对象或数组引用,每次渲染产生新引用从而触发新查询。检测方法:Network 面板显示相同请求超过 10 次/秒;React DevTools Profiler 显示持续重渲染。修复方式是使用稳定的原始值(如 user.id)。若 key 必须包含对象,可用 useMemo 生成稳定 key,并设置 structuralSharing: false。

  • 陈旧数据陷阱:原因是 staleTime: Infinity,数据永远不会被标记为陈旧。检测方法:Network 面板在初始加载后显示零请求;用户反馈“数据不更新”但开发者无法复现(开发者频繁刷新会清空缓存)。修复:使用合理的 staleTime;若仍陈旧,调用 queryClient.invalidateQueries({ queryKey: ['your-key'] })。

  • 过度失效:原因是无过滤条件的 queryClient.invalidateQueries() 会清空整个缓存,导致所有查询重取。修复:使用带 queryKey 的定向失效。

决策框架

乐观更新 vs 失效:

  • 简单列表追加/前置(添加待办、添加评论):使用乐观更新(useMutationState)。
  • 复杂计算数据(聚合、过滤、排序):使用失效,让服务端计算。
  • 存在冲突风险(多用户):使用失效,让服务端解决。
  • 必须即时反馈(切换点赞、收藏):乐观更新 + 出错回滚。

React Query vs SWR:

  • 优先 React Query:需要细粒度 gc/stale 控制、复杂失效模式、带回滚的乐观更新、无限查询/分页、已在 TanStack 生态中。
  • 优先 SWR:偏好更简单的 API(更少配置)、更小的包体积、Next.js(一方支持)、简单仪表盘场景。

SSR 水合(Next.js App Router)

不匹配模式:服务端渲染“Loading...”,客户端已有缓存数据,导致水合错误。

推荐预取模式:在服务端组件中创建 QueryClient,使用 prefetchQuery 预取,再用 dehydrate 与 HydrationBoundary 包裹客户端组件;客户端组件中直接使用 useQuery,无需 isPending 检查,因为数据已由服务端预取保证。

水合不匹配的备选方案:通过 props 以 initialData 传入,而不是使用 prefetch。

调试命令

  • 查找重取循环:在 QueryClient 的 defaultOptions 中加 onSuccess 计数,1 秒内计数超过 10 即为无限循环。
  • 检查缓存状态:queryClient.getQueryState(['todos']) 查看 isInvalidated。
  • 彻底清缓存:queryClient.removeQueries、queryClient.refetchQueries 或 queryClient.clear()。
  • 查找代码库中残留的 v4 属性名:grep -r "cacheTime\|isLoading\|keepPreviousData" src/。
  • 添加 <ReactQueryDevtools initialIsOpen={false} /> 可视化缓存状态、重取次数与陈旧情况。

何时加载完整参考

  • 同时使用 3 个以上 v5 特性(useMutationState、throwOnError、无限查询、suspense 模式)时,阅读 references/v5-features.md。
  • 迁移包含 10 个以上查询用法的代码库或运行 codemod 时,阅读 references/migration-guide.md。
  • 单个破坏性变更修复、基础排错或简单乐观更新无需加载参考,以上内容已覆盖。

资源

官方文档:https://tanstack.com/query/latest