关于 web-server-state-react-query
面向在 React 应用中管理服务器数据的 React Query 实践指南,涵盖缓存行为、类型化查询选项、错误、变更和分页。适用于由 API 架构生成客户端或手写查询的场景。
主要功能
- 01生成类型化查询选项
- 02查询键与缓存配置
- 03集中处理查询和变更错误
- 04变更操作与缓存失效
- 05无限查询与游标分页
适用场景
- 01根据 API 架构生成查询
- 02管理数据新鲜度与缓存清理
- 03跨组件处理错误
- 04构建分页数据视图
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill web-server-state-react-query安装后在 Claude Code 中输入/web-server-state-react-query确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/core.mderror-handling.mdinfinite-queries.mdmutations.mdsuspense.md详细介绍
React Query 模式
快速指南: React Query 管理服务端缓存——获取了哪些数据、数据会保持新鲜多久,以及何时被清除。每个条目都由其
queryKey标识,而staleTime和gcTime是两个独立的计时器,并非同一个。v5 从useQuery中移除了onError/onSuccess/onSettled,因此错误副作用应放在组件中,或放在共享的QueryCache上。
详细资源:
- examples/core.md — 生成的客户端输出、Provider 和身份验证设置、
staleTime与gcTime的区别、使用查询选项、请求超时 - examples/error-handling.md — 组件状态、全局处理器、重试退避、错误边界
- examples/mutations.md —
useMutation、失效处理、通过 UI 或缓存进行乐观更新 - examples/infinite-queries.md —
useInfiniteQuery、游标分页、maxPages - examples/suspense.md —
useSuspenseQuery、useSuspenseQueries、依赖型 Suspense 查询 - reference.md — 配置与错误策略查询、v4 → v5 迁移表
适用路径
- API 有可用于生成的 Schema — 从中生成类型、服务函数和查询选项,然后调用
useQuery(generatedOptions())。键和类型来自同一来源,因此 Schema 变更会以编译错误的形式显现。模式 1。 - 没有 Schema,或 Schema 不受你控制 — 手动编写查询函数,在解析边界验证响应,并有意设计查询键,因为没有任何内容会自动生成它们。从模式 2 开始的所有内容均适用,无需调整。
<critical_requirements>
编写 React Query 代码前
从环境变量读取 API 基础 URL。 同一构建即可在所有环境中运行。
Schema 变更时重新生成客户端,并提交生成的输出。 重新生成的类型会让不兼容的 API 变更在代码审查时成为编译错误,而不是在生产环境中造成运行时错误。
</critical_requirements>
自动检测: useQuery、useMutation、useInfiniteQuery、useSuspenseQuery、QueryClient、QueryClientProvider、QueryCache、MutationCache、queryKey、queryOptions、staleTime、gcTime、invalidateQueries、placeholderData、initialPageParam、openapi-ts
适用范围:
- 缓存、失效处理和重新获取服务端数据
- QueryClient 默认值、重试策略和全局错误处理
- 从 API Schema 生成类型化查询选项
- 依赖型、条件型和防抖查询
其他部分处理:
- 从未来自服务端的纯客户端状态——此技能会缓存响应,不会处理用户输入的状态
- 编写 API Schema;此技能使用已有的 Schema
- 通过图查询语言访问的 API;其客户端缓存的是规范化实体,而不是按键缓存整个响应
- 长期运行的套接字流——此缓存采用请求/响应模式
核心模式
模式 1:生成查询选项
将生成器指向 Schema,让它同时生成类型、服务函数和查询选项。查询键也会一并生成,因此不同调用点不会对查询键产生分歧。
// openapi-ts.config.ts
export default defineConfig({
input: "./openapi.yaml",
output: "src/api-client",
plugins: ["@hey-api/typescript", "@hey-api/sdk", "@tanstack/react-query"],
});
从 @hey-api/openapi-ts v0.73 打包了一个 fetch 客户端——只有在需要自定义客户端选项时,才显式指定客户端插件。
完整代码:examples/core.md
模式 2:客户端和缓存配置
在一个 Provider 中一次性配置基础 URL 和 QueryClient 默认值。在 useState 中创建客户端,这样重新渲染就不会替换树下的缓存。
const FIVE_MINUTES_MS = 5 * 60 * 1000;
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: { staleTime: FIVE_MINUTES_MS, refetchOnWindowFocus: false },
},
}),
);
client.setConfig({ baseUrl: process.env.API_BASE_URL ?? "" });
无论如何获取数据,QueryClient 部分都适用。client 是生成的客户端,它的 setConfig() 会合并到现有配置中,而不是替换配置,因此可以在不同调用点分别设置身份验证和自定义 fetch;手写的查询函数则从原有位置读取基础 URL。
完整代码:examples/core.md — Provider 设置、静态和动态身份验证,以及一个超时后中止请求的 fetch 包装器
模式 3:使用和覆盖查询选项
直接调用生成的选项;当某个调用点需要不同的策略时,对选项进行展开。
const { data, isPending, error } = useQuery(getFeaturesOptions());
const TEN_MINUTES_MS = 10 * 60 * 1000;
const { data: slow } = useQuery({
...getFeaturesOptions(),
staleTime: TEN_MINUTES_MS,
enabled: someCondition,
});
不使用生成器时,queryOptions 辅助函数可以手动定义同样的内容,并提供类型支持:
import { queryOptions } from "@tanstack/react-query";
const featuresOptions = () =>
queryOptions({ queryKey: ["features"], queryFn: getFeatures });
无论哪种方式,键和函数都只在一个地方声明,这样才能避免两个调用点为一个端点打开两个缓存条目,也能确保 invalidateQueries 不会只触及其中一个。展开选项会保留键,因此覆盖选项会共用同一个条目,而不是新增一个条目。
完整代码:examples/core.md
模式 4:错误处理
组件级 error 用于处理用户正在查看的查询;QueryCache 和 MutationCache 则可在一个地方处理其他所有错误。
new QueryClient({
queryCache: new QueryCache({
onError: (error, query) => {
// A query that already held data failed a background refetch — the component shows stale
// data and no error, so the notification is the only signal.
if (query.state.data !== undefined) notify(error.message);
},
}),
mutationCache: new MutationCache({
onError: () => notify("Operation failed."),
}),
});
完整代码:examples/error-handling.md
模式 5:条件查询和防抖查询
以防抖后的值作为查询键,并通过 enabled 控制查询,避免空搜索词发送到网络。
const DEBOUNCE_DELAY_MS = 500;
const debouncedTerm = useDebounce(searchTerm, DEBOUNCE_DELAY_MS);
const { data } = useQuery({
queryKey: ["search", debouncedTerm],
queryFn: () => searchApi(debouncedTerm),
enabled: debouncedTerm.length > 0,
});
如果改用原始搜索词作为键,每次按键都会缓存一个条目。
<red_flags>
注意事项
会在运行时出错:
- 在
useQuery上使用onError/onSuccess/onSettled—— 这些选项已在 v5 中移除,因此副作用不会运行,且不会发出提示。请使用组件的error或QueryCache.onError。 - 在查询函数内部调用
client.setConfig()—— 这会修改所有正在进行的请求共用的配置,因此并发查询会争抢基础 URL。 - 使用
AbortController设置超时,却未在成功路径中调用clearTimeout—— 响应返回后,计时器仍会使闭包保持存活。 - 渲染的查询既没有
error分支,上层也没有错误边界 —— 请求被拒绝时会导致整个子树崩溃。
容易引发意外的行为:
setConfig()是合并而非替换,因此部分调用会静默保留先前的身份验证配置。- v5 中服务端重试的默认次数为
0,而 v4 会重试三次。 fetch超时与staleTime和gcTime无关 —— 前者限制单个请求的时长,后两者限制缓存条目的生命周期。- 对本地模拟接口保留
retry,会让一个仅仅是缺失的模拟接口导致缓慢失败。 - 为已有 Schema 类型定义的响应手写接口,会悄然产生类型偏差:后端新增的字段既不在类型中,也不会出现在任何需要它的渲染结果中。
- 在生成的选项旁边手写
useQuery包装器,会让同一端点拥有两个查询键和两个缓存条目,因此失效其中一个并不会使另一个变为最新。
</red_flags>