用途
该技能面向 TanStack Query v5+(即 React Query),用于处理异步操作、数据获取、缓存、变更、无限查询与乐观更新。它适用于任何能从缓存或状态管理中获益的异步操作,包括服务端数据获取、localStorage 读取、本地计算与文件操作等。
不覆盖的范围包括:URL 状态管理(应使用 nuqs)、分组同步状态(应使用 ahooks.useSetState)、localStorage 持久化(应使用 ahooks.useLocalStorageState)。
前提假设
- TanStack Query v5+
- React 18+,支持 hooks
- TypeScript v5+(用于类型推断)
- 在共享查询模块中手写查询键元组(query-key-factory 为可选项)
- 在应用根部配置 QueryClientProvider
主要原则
- 对任何能从缓存或状态管理中获益的异步操作使用 TanStack Query
- 优先使用手写查询键元组,与 hooks 同处或放在共享的 queries/ 模块中
- 绝不手动管理 isLoading、error 或 isError 状态(由 hooks 提供)
- queryFn 可以是任何返回 Promise 的函数(不限于 HTTP 调用)
- 无论数据源如何,所有 TanStack 特性行为一致:缓存、去重、后台重新获取、stale-while-revalidate
约束
必须(MUST):
- 使用稳定、分层的查询键(手写元组或可选的 query-key-factory)
- 对异步操作使用 TanStack Query hooks(绝不手动管理加载/错误状态)
应当(SHOULD):
- 对任何能从缓存中获益的异步操作使用 TanStack Query(HTTP、localStorage 读取、本地计算、文件操作)
- 将查询逻辑提取到自定义 hooks 中以复用
- 将多个查询组合为内聚的 hooks
- 使用 TypeScript 泛型实现类型安全查询:useQuery<ResponseType>(...)
- 在 provider 级别配置 QueryClient 默认值(retry、staleTime)
- 当项目已统一使用 @lukemorales/query-key-factory 时使用它
避免(AVOID):
- 在失效操作中硬编码不相关的查询键而没有共享前缀
- 手动管理加载/错误状态(使用 hook 提供的状态)
- 用于 URL 可共享状态(改用 nuqs)
- 用于分组同步状态(改用 ahooks.useSetState)
- 用于无需缓存的一次性 promise(在事件处理器中使用普通 async/await)
与其他方案的交互
- 与 nuqs-v2 互补,用于 URL 状态管理(查询可依赖 URL 参数)
- 与 ahooks-v3 互补,用于同步状态(useSetState 用于表单状态,useLocalStorageState 用于持久化)
- 可与 OpenAPI 生成的 API 客户端配合使用;React Query hooks 针对这些客户端手写
- 属于状态管理决策树的一部分(参见 React 规则)
模式
手写查询键
同处、稳定的查询键:
export const userKeys = { all: ['users'] as const, detail: (id: string) => [...userKeys.all, id] as const, list: (filters?: Filters) => [...userKeys.all, 'list', filters] as const, }
// 用法 import { useQuery } from '@tanstack/react-query' import { userKeys } from '@/queries/users'
const { data, isLoading, error } = useQuery({ queryKey: userKeys.detail(userId), queryFn: () => fetchUser(userId), })
可选的查询键工厂
当项目已使用 @lukemorales/query-key-factory 时:
import { createQueryKeys } from '@lukemorales/query-key-factory'
export const users = createQueryKeys('users', { detail: (id: string) => ({ queryKey: [id], queryFn: () => fetchUser(id), }), })
通用 queryFn 模式
TanStack Query 接受任何异步 queryFn——不限于 HTTP 调用:
// 服务端数据获取(传统方式) const { data } = useQuery({ queryKey: ['users', userId], queryFn: () => fetchUser(userId), })
// 本地计算(无网络) const { data } = useQuery({ queryKey: ['fibonacci', n], queryFn: () => computeFibonacci(n), })
// localStorage 读取 const { data: settings } = useQuery({ queryKey: ['user-settings'], queryFn: () => Promise.resolve( JSON.parse(localStorage.getItem('settings') || '{}') ), })
// 文件操作 const { data } = useQuery({ queryKey: ['file-content', path], queryFn: () => readFile(path), })
何时使用:任何能从自动缓存、过期管理或重复执行中获益的异步操作。
何时不使用:无需缓存的一次性 promise(在事件处理器中使用普通 async/await)。
变更模式
带自动失效的变更:
import { useMutation, useQueryClient } from '@tanstack/react-query' import { userKeys } from '@/queries/users'
function useCreateUser() { const queryClient = useQueryClient()
return useMutation({ mutationFn: async (data: CreateUserInput) => { const response = await createUser(data) return response }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: userKeys.all }) }, }) }
无限查询模式
带无限滚动的分页:
import { useInfiniteQuery } from '@tanstack/react-query' import { users } from '@/queries/users'
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, } = useInfiniteQuery({ queryKey: ['users', 'infinite'], queryFn: ({ pageParam = 0 }) => fetchUsers({ page: pageParam }), getNextPageParam: (lastPage, pages) => lastPage.nextPage, initialPageParam: 0, })
QueryClient 配置
在 provider 级别配置默认值:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { useState } from 'react'
function Providers({ children }: { children: ReactNode }) { const [queryClient] = useState(() => new QueryClient({ defaultOptions: { queries: { staleTime: 60 * 1000, // 1 分钟 retry: 1, refetchOnWindowFocus: false, }, }, }))
return ( <QueryClientProvider client={queryClient}> {children} </QueryClientProvider> ) }
手写 Hook 模式
将生成的 OpenAPI 客户端包装在自定义 hooks 中:
import { useQuery } from '@tanstack/react-query' import { apiClient } from '@/lib/api-client'
export function useHealthCheck() { return useQuery({ queryKey: ['health'], queryFn: () => apiClient.GET('/health').then(r => r.data), }) }
自定义 Hook 模式
将查询逻辑提取到可复用的 hooks 中:
// hooks/useUser.ts import { useQuery } from '@tanstack/react-query' import { userKeys } from '@/queries/users'
export function useUser(userId: string) { return useQuery({ queryKey: userKeys.detail(userId), queryFn: () => fetchUser(userId), }) }
状态管理决策树
- URL 可共享状态 → 使用 nuqs(过滤器、搜索、标签页、分页)
- 不在 URL 中的分组状态 → 使用 ahooks.useSetState(表单状态、游戏引擎、临时 UI)
- 异步操作 → 使用 TanStack Query(数据获取、变更、缓存)
- localStorage 持久化 → 使用 ahooks.useLocalStorageState(偏好设置、设置项)
- 简单独立状态 → 使用 useState(少见,优先考虑其他选项)
使用 TanStack Query 的场景:服务端数据获取、变更、乐观更新、后台重新获取、缓存、任何能从状态管理中获益的异步操作。
不使用 TanStack Query 的场景:URL 可共享状态(使用 nuqs)、分组同步状态(使用 ahooks.useSetState)、无需缓存的一次性 promise。
参考资料
- TanStack Query 文档 - 官方文档
- Query Key Factory - 查询键工厂库
- React 规则 - 状态管理决策树与模式
- ahooks - 用于同步状态的工具 hooks
- OpenAPI 集成 - 生成的客户端模式
限制
资料未提供该技能的具体安装命令、收费方式、价格、平台支持范围或实际使用效果等信息,这些内容暂未提供。