tanstack-query-v5
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSkill: tanstack-query
Skill: tanstack-query
Scope
范围
- Applies to: TanStack Query v5+ for async operations, data fetching, caching, mutations, infinite queries, optimistic updates
- Does NOT cover: URL state management (use nuqs), grouped synchronous state (use ahooks.useSetState), localStorage persistence (use ahooks.useLocalStorageState)
- 适用于:TanStack Query v5+ 的异步操作、数据获取、缓存、mutations、infinite queries、乐观更新
- 不涵盖:URL 状态管理(use nuqs)、分组同步状态(ahooks.useSetState)、localStorage 持久化(ahooks.useLocalStorageState)
Assumptions
前提条件
- TanStack Query v5+
- React 18+ with hooks support
- TypeScript v5+ (for type inference)
- Handwritten query-key tuples in shared query modules (query-key-factory is optional)
- QueryClientProvider configured at app root
- TanStack Query v5+
- 支持 hooks 的 React 18+
- TypeScript v5+(用于类型推断)
- 在共享 query modules 中手写 query-key 元组(query-key-factory 可选)
- 已在 app root 配置 QueryClientProvider
Principles
原则
- Use TanStack Query for ANY async operation that benefits from caching or state management
- Prefer handwritten query-key tuples colocated with hooks or in a shared module
queries/ - Never manually manage ,
isLoading, orerrorstates (provided by hooks)isError - can be any Promise-returning function (not just HTTP calls)
queryFn - All TanStack features work identically regardless of data source: caching, deduping, background refetching, stale-while-revalidate
- 对任何可从缓存或状态管理中获益的异步操作使用 TanStack Query
- 优先使用与 hooks 放在一起或置于共享 模块中的手写 query-key 元组
queries/ - 绝不手动管理 、
isLoading或error状态(这些状态由 hooks 提供)isError - 可以是任何返回 Promise 的函数(不仅仅是 HTTP calls)
queryFn - 无论 data source 如何,所有 TanStack 特性都以相同方式工作:caching、deduping、background refetching、stale-while-revalidate
Constraints
约束
MUST
必须
- Use stable, hierarchical query keys (handwritten tuples or optional query-key-factory)
- Use TanStack Query hooks for async operations (never manually manage loading/error states)
- 使用稳定、分层的 query keys(手写元组或可选的 query-key-factory)
- 对异步操作使用 TanStack Query hooks(绝不手动管理 loading/error 状态)
SHOULD
应该
- Use TanStack Query for any async operation that benefits from caching (HTTP, localStorage reads, local computations, file operations)
- Extract query logic into custom hooks for reusability
- Combine multiple queries into cohesive hooks
- Use TypeScript generics for type-safe queries:
useQuery<ResponseType>(...) - Configure QueryClient defaults (retry, staleTime) at provider level
- Use when the project already standardizes on it
@lukemorales/query-key-factory
- 对任何可从缓存中获益的异步操作使用 TanStack Query(HTTP、localStorage 读取、本地计算、文件操作)
- 将 query 逻辑提取到 custom hooks 中以便复用
- 将多个 queries 合并为内聚的 hooks
- 使用 TypeScript generics 实现类型安全的 queries:
useQuery<ResponseType>(...) - 在 provider 层配置 QueryClient defaults(retry、staleTime)
- 当项目已经标准化使用 时使用它
@lukemorales/query-key-factory
AVOID
避免
- Hardcoding unrelated query keys in invalidations without a shared prefix
- Manually managing loading/error states (use hook-provided states)
- Using for URL-shareable state (use nuqs instead)
- Using for grouped synchronous state (use ahooks.useSetState instead)
- Using for one-off promises without caching needs (use plain async/await in event handlers)
- 在 invalidations 中硬编码不相关且没有共享 prefix 的 query keys
- 手动管理 loading/error 状态(应使用 hooks 提供的状态)
- 用于可通过 URL 分享的状态(应改用 nuqs)
- 用于分组的同步状态(应改用 ahooks.useSetState)
- 用于无需缓存的一次性 Promise(在事件处理器中使用普通 async/await)
Interactions
交互关系
- Complements nuqs-v2 for URL state management (queries can depend on URL params)
- Complements ahooks-v3 for synchronous state (useSetState for form state, useLocalStorageState for persistence)
- Works with OpenAPI-generated API clients; React Query hooks are handwritten against those clients
- Part of state management decision tree (see React rules)
- 与 nuqs-v2 配合用于 URL 状态管理(queries 可以依赖 URL params)
- 与 ahooks-v3 配合用于同步状态(useSetState 用于表单状态,useLocalStorageState 用于持久化)
- 可与 OpenAPI 生成的 API clients 配合使用;React Query hooks 基于这些 clients 手写
- 是状态管理 decision tree 的一部分(参见 React rules)
Patterns
模式
Handwritten Query Keys
手写 Query Keys
Colocated, stable query keys:
typescript
export const userKeys = {
all: ['users'] as const,
detail: (id: string) => [...userKeys.all, id] as const,
list: (filters?: Filters) => [...userKeys.all, 'list', filters] as const,
}
// Usage
import { useQuery } from '@tanstack/react-query'
import { userKeys } from '@/queries/users'
const { data, isLoading, error } = useQuery({
queryKey: userKeys.detail(userId),
queryFn: () => fetchUser(userId),
})位于同一位置且稳定的 query keys:
typescript
export const userKeys = {
all: ['users'] as const,
detail: (id: string) => [...userKeys.all, id] as const,
list: (filters?: Filters) => [...userKeys.all, 'list', filters] as const,
}
// Usage
import { useQuery } from '@tanstack/react-query'
import { userKeys } from '@/queries/users'
const { data, isLoading, error } = useQuery({
queryKey: userKeys.detail(userId),
queryFn: () => fetchUser(userId),
})Optional Query Key Factory
可选 Query Key Factory
When the project already uses :
@lukemorales/query-key-factorytypescript
import { createQueryKeys } from '@lukemorales/query-key-factory'
export const users = createQueryKeys('users', {
detail: (id: string) => ({
queryKey: [id],
queryFn: () => fetchUser(id),
}),
})当项目已经使用 时:
@lukemorales/query-key-factorytypescript
import { createQueryKeys } from '@lukemorales/query-key-factory'
export const users = createQueryKeys('users', {
detail: (id: string) => ({
queryKey: [id],
queryFn: () => fetchUser(id),
}),
})Versatile queryFn Pattern
通用 queryFn 模式
TanStack Query accepts ANY async —not just HTTP calls:
queryFntypescript
// Server data fetching (traditional)
const { data } = useQuery({
queryKey: ['users', userId],
queryFn: () => fetchUser(userId),
})
// Local computation (no network)
const { data } = useQuery({
queryKey: ['fibonacci', n],
queryFn: () => computeFibonacci(n),
})
// localStorage read
const { data: settings } = useQuery({
queryKey: ['user-settings'],
queryFn: () => Promise.resolve(
JSON.parse(localStorage.getItem('settings') || '{}')
),
})
// File operation
const { data } = useQuery({
queryKey: ['file-content', path],
queryFn: () => readFile(path),
})When to use: Any async operation that benefits from automatic caching, stale management, or repeated execution
When NOT to use: One-off promises without caching needs (use plain async/await in event handlers)
TanStack Query 接受任何异步 ,而不仅仅是 HTTP calls:
queryFntypescript
// Server data fetching (traditional)
const { data } = useQuery({
queryKey: ['users', userId],
queryFn: () => fetchUser(userId),
})
// Local computation (no network)
const { data } = useQuery({
queryKey: ['fibonacci', n],
queryFn: () => computeFibonacci(n),
})
// localStorage read
const { data: settings } = useQuery({
queryKey: ['user-settings'],
queryFn: () => Promise.resolve(
JSON.parse(localStorage.getItem('settings') || '{}')
),
})
// File operation
const { data } = useQuery({
queryKey: ['file-content', path],
queryFn: () => readFile(path),
})何时使用:任何可从自动缓存、stale 状态管理或重复执行中获益的异步操作
何时不使用:无需缓存的一次性 Promise(在事件处理器中使用普通 async/await)
Mutation Pattern
Mutation 模式
Mutations with automatic invalidation:
typescript
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 })
},
})
}带自动 invalidation 的 mutations:
typescript
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 })
},
})
}Infinite Query Pattern
Infinite Query 模式
Pagination with infinite scroll:
typescript
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,
})使用无限滚动实现分页:
typescript
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 Configuration
QueryClient 配置
Configure defaults at provider level:
typescript
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 minute
retry: 1,
refetchOnWindowFocus: false,
},
},
}))
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
)
}在 provider 层配置 defaults:
typescript
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 minute
retry: 1,
refetchOnWindowFocus: false,
},
},
}))
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
)
}Handwritten Hook Pattern
手写 Hook 模式
Wrap the generated OpenAPI client in custom hooks:
typescript
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),
})
}将生成的 OpenAPI client 包装到 custom hooks 中:
typescript
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),
})
}Custom Hook Pattern
Custom Hook 模式
Extract query logic into reusable hooks:
typescript
// 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),
})
}将 query 逻辑提取到可复用 hooks 中:
typescript
// 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),
})
}State Management Decision Tree
状态管理 Decision Tree
- URL-shareable state → Use (filters, search, tabs, pagination)
nuqs - Grouped state not in URL → Use (form state, game engine, ephemeral UI)
ahooks.useSetState - Async operations → Use TanStack Query (data fetching, mutations, caching)
- localStorage persistence → Use (preferences, settings)
ahooks.useLocalStorageState - Simple independent state → Use (rare, prefer other options)
useState
Use TanStack Query for: Server data fetching, mutations, optimistic updates, background refetching, caching, any async operation that benefits from state management
Don't use TanStack Query for: URL-shareable state (use nuqs), grouped synchronous state (use ahooks.useSetState), one-off promises without caching needs
- 可通过 URL 分享的状态 → 使用 (筛选、搜索、tabs、分页)
nuqs - 不在 URL 中的分组状态 → 使用 (表单状态、游戏引擎、临时 UI)
ahooks.useSetState - 异步操作 → 使用 TanStack Query(数据获取、mutations、缓存)
- localStorage 持久化 → 使用 (首选项、设置)
ahooks.useLocalStorageState - 简单的独立状态 → 使用 (很少使用,优先选择其他方案)
useState
使用 TanStack Query 处理:服务器数据获取、mutations、乐观更新、后台重新获取、缓存,以及任何可从状态管理中获益的异步操作
不要使用 TanStack Query 处理:可通过 URL 分享的状态(改用 nuqs)、分组的同步状态(改用 ahooks.useSetState)、无需缓存的一次性 Promise
References
参考
- TanStack Query documentation - Official documentation
- Query Key Factory - Query key factory library
- React rules - State management decision tree and patterns
- ahooks - Utility hooks for synchronous state
- OpenAPI Integration - Generated client patterns
- TanStack Query 文档 - 官方文档
- Query Key Factory - Query key factory 库
- React rules - 状态管理决策树和模式
- ahooks - 用于同步状态的实用 hooks
- OpenAPI Integration - 生成的 client 模式