SuperAPI AI 工具集

tanstack-query-v5

编程与开发

查看项目

更新于 10-27 16:23

技能介绍

用途

该技能面向 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 集成 - 生成的客户端模式

限制

资料未提供该技能的具体安装命令、收费方式、价格、平台支持范围或实际使用效果等信息,这些内容暂未提供。