执行契约
- 适用场景
- 查询可能因路由切换、参数变化或手动操作而过期,底层请求支持 AbortSignal。
- 不要使用
- 底层操作不可取消或取消会破坏必须完成的写入时,不要伪造取消语义。
- 前置检查
- 确认 queryFn 接收 signal,并把同一实例传给所有相关 I/O。
- 验证结果
- 在慢请求期间切换参数并观察旧请求中止;缓存应恢复到请求前状态且新请求完成。
- 失败模式
- 网络仍运行说明 signal 未传到底层;空数据成功说明 AbortError 被捕获并转换成普通返回值。
- 安全边界
- 取消客户端等待不会撤销服务端已经完成的副作用,因此不要把查询取消当作事务回滚。
默认行为
查询变为未使用时,其 Promise 默认仍可完成并把数据写入缓存。只有底层请求消费了 signal,取消才中止 Promise,并把 Query 状态恢复到发起请求前的状态。
请求契约
queryFn 从参数解构 signal,并传给每个相关 fetch。保持 AbortError 的原始拒绝,不要捕获后返回空数据,否则 Query 会把取消伪装成成功。
import { queryOptions } from '@tanstack/react-query'
export const issueOptions = (issueId: string) => queryOptions({
queryKey: ['issues', 'detail', issueId] as const,
queryFn: async ({ signal }) => {
const response = await fetch(`/api/issues/${issueId}`, { signal })
if (!response.ok) throw new Error(`Issue request failed: ${response.status}`)
return response.json()
},
})限制与手动取消
queryClient.cancelQueries 可以按 queryKey 手动取消;消费 signal 后也会取消底层 Promise。当前官方文档明确指出 Suspense 查询 hooks 不支持取消,因此不要把相同交互假设直接套到 useSuspenseQuery。