tanstack-query

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TanStack Query (React Query) v5

TanStack Query (React Query) v5

Powerful asynchronous state management for React. TanStack Query makes fetching, caching, synchronizing, and updating server state in your React applications a breeze.
为React提供强大的异步状态管理能力。TanStack Query让React应用中的数据获取、缓存、同步及服务端状态更新变得轻而易举。

When to Use This Skill

何时使用该工具

  • Fetching data from REST APIs or GraphQL endpoints
  • Managing server state and cache lifecycle
  • Implementing mutations (create, update, delete operations)
  • Building infinite scroll or load-more patterns
  • Handling optimistic UI updates
  • Synchronizing data across components
  • Implementing background data refetching
  • Managing complex async state without Redux or other state managers
  • 从REST API或GraphQL端点获取数据
  • 管理服务端状态与缓存生命周期
  • 实现变更操作(创建、更新、删除)
  • 构建无限滚动或加载更多模式
  • 处理乐观UI更新
  • 跨组件同步数据
  • 实现后台数据重新获取
  • 无需Redux或其他状态管理器即可管理复杂异步状态

Quick Start Workflow

快速开始流程

1. Installation

1. 安装

bash
npm install @tanstack/react-query
bash
npm install @tanstack/react-query

or

or

pnpm add @tanstack/react-query
pnpm add @tanstack/react-query

or

or

yarn add @tanstack/react-query
undefined
yarn add @tanstack/react-query
undefined

2. Setup QueryClient

2. 配置QueryClient

Wrap your application with
QueryClientProvider
:
tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
    </QueryClientProvider>
  );
}
使用
QueryClientProvider
包裹你的应用:
tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
    </QueryClientProvider>
  );
}

3. Basic Query

3. 基础查询

tsx
import { useQuery } from '@tanstack/react-query';

function TodoList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => {
      const res = await fetch('https://api.example.com/todos');
      if (!res.ok) throw new Error('Network response was not ok');
      return res.json();
    },
  });

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}
tsx
import { useQuery } from '@tanstack/react-query';

function TodoList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => {
      const res = await fetch('https://api.example.com/todos');
      if (!res.ok) throw new Error('Network response was not ok');
      return res.json();
    },
  });

  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>错误:{error.message}</div>;

  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

4. Basic Mutation

4. 基础变更

tsx
import { useMutation, useQueryClient } from '@tanstack/react-query';

function CreateTodo() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: async (newTodo) => {
      const res = await fetch('https://api.example.com/todos', {
        method: 'POST',
        body: JSON.stringify(newTodo),
        headers: { 'Content-Type': 'application/json' },
      });
      return res.json();
    },
    onSuccess: () => {
      // Invalidate and refetch todos
      queryClient.invalidateQueries({ queryKey: ['todos'] });
    },
  });

  return (
    <button onClick={() => mutation.mutate({ title: 'New Todo' })}>
      {mutation.isPending ? 'Creating...' : 'Create Todo'}
    </button>
  );
}
tsx
import { useMutation, useQueryClient } from '@tanstack/react-query';

function CreateTodo() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: async (newTodo) => {
      const res = await fetch('https://api.example.com/todos', {
        method: 'POST',
        body: JSON.stringify(newTodo),
        headers: { 'Content-Type': 'application/json' },
      });
      return res.json();
    },
    onSuccess: () => {
      // 失效并重新获取todos数据
      queryClient.invalidateQueries({ queryKey: ['todos'] });
    },
  });

  return (
    <button onClick={() => mutation.mutate({ title: 'New Todo' })}>
      {mutation.isPending ? '创建中...' : '创建待办事项'}
    </button>
  );
}

Core Concepts

核心概念

Query Keys

查询键(Query Keys)

Query keys uniquely identify queries and are used for caching. They must be arrays.
tsx
// Simple key
useQuery({ queryKey: ['todos'], queryFn: fetchTodos });

// Key with variables
useQuery({ queryKey: ['todo', todoId], queryFn: () => fetchTodo(todoId) });

// Hierarchical keys
useQuery({ queryKey: ['todos', 'list', { filters, page }], queryFn: fetchTodos });
Query key matching:
  • ['todos']
    - exact match
  • ['todos', { page: 1 }]
    - exact match with object
  • { queryKey: ['todos'] }
    - matches all queries starting with 'todos'
查询键用于唯一标识查询,是缓存的依据,必须为数组格式。
tsx
// 简单键
useQuery({ queryKey: ['todos'], queryFn: fetchTodos });

// 带变量的键
useQuery({ queryKey: ['todo', todoId], queryFn: () => fetchTodo(todoId) });

// 层级键
useQuery({ queryKey: ['todos', 'list', { filters, page }], queryFn: fetchTodos });
查询键匹配规则:
  • ['todos']
    - 精确匹配
  • ['todos', { page: 1 }]
    - 包含对象的精确匹配
  • { queryKey: ['todos'] }
    - 匹配所有以'todos'开头的查询

Query Functions

查询函数(Query Functions)

Query functions must return a promise that resolves data or throws an error:
tsx
// Using fetch
queryFn: async () => {
  const res = await fetch(url);
  if (!res.ok) throw new Error('Failed to fetch');
  return res.json();
}

// Using axios
queryFn: () => axios.get(url).then(res => res.data)

// With query key access
queryFn: ({ queryKey }) => {
  const [_, todoId] = queryKey;
  return fetchTodo(todoId);
}
查询函数必须返回一个Promise,该Promise需解析数据或抛出错误:
tsx
// 使用fetch
queryFn: async () => {
  const res = await fetch(url);
  if (!res.ok) throw new Error('Failed to fetch');
  return res.json();
}

// 使用axios
queryFn: () => axios.get(url).then(res => res.data)

// 访问查询键
queryFn: ({ queryKey }) => {
  const [_, todoId] = queryKey;
  return fetchTodo(todoId);
}

Important Defaults

重要默认配置

Understanding defaults is crucial for optimal usage:
  • staleTime: 0 - Queries become stale immediately by default
  • gcTime: 5 minutes - Unused/inactive cache data remains in memory for 5 minutes
  • retry: 3 - Failed queries retry 3 times with exponential backoff
  • refetchOnWindowFocus: true - Queries refetch when window regains focus
  • refetchOnReconnect: true - Queries refetch when network reconnects
tsx
// Override defaults globally
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutes
      gcTime: 1000 * 60 * 10, // 10 minutes
    },
  },
});

// Or per query
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  staleTime: 1000 * 60, // 1 minute
  retry: 5,
});
理解默认配置对优化使用至关重要:
  • staleTime: 0 - 默认情况下,查询结果会立即变为“过时”状态
  • gcTime: 5分钟 - 未使用/非活跃的缓存数据会在内存中保留5分钟
  • retry: 3 - 失败的查询会自动重试3次,采用指数退避策略
  • refetchOnWindowFocus: true - 当窗口重新获得焦点时,会重新获取查询数据
  • refetchOnReconnect: true - 网络重新连接时,会重新获取查询数据
tsx
// 全局覆盖默认配置
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5分钟
      gcTime: 1000 * 60 * 10, // 10分钟
    },
  },
});

// 或针对单个查询配置
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  staleTime: 1000 * 60, // 1分钟
  retry: 5,
});

Query Status and Fetch Status

查询状态与获取状态

Queries have two important states:
Query Status:
  • pending
    - No cached data, query is executing
  • error
    - Query encountered an error
  • success
    - Query succeeded and data is available
Fetch Status:
  • fetching
    - Query function is executing
  • paused
    - Query wants to fetch but is paused (offline)
  • idle
    - Query is not fetching
tsx
const { data, status, fetchStatus, isLoading, isFetching } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
});

// isLoading = status === 'pending'
// isFetching = fetchStatus === 'fetching'
查询包含两个重要状态:
查询状态:
  • pending
    - 无缓存数据,查询正在执行
  • error
    - 查询遇到错误
  • success
    - 查询成功,数据可用
获取状态:
  • fetching
    - 查询函数正在执行
  • paused
    - 查询需要执行但已暂停(离线状态)
  • idle
    - 查询未在执行
tsx
const { data, status, fetchStatus, isLoading, isFetching } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
});

// isLoading = status === 'pending'
// isFetching = fetchStatus === 'fetching'

Query Invalidation

查询失效

Mark queries as stale to trigger refetches:
tsx
const queryClient = useQueryClient();

// Invalidate all todos queries
queryClient.invalidateQueries({ queryKey: ['todos'] });

// Invalidate specific query
queryClient.invalidateQueries({ queryKey: ['todo', todoId] });

// Invalidate and refetch immediately
queryClient.invalidateQueries({
  queryKey: ['todos'],
  refetchType: 'active' // only refetch active queries
});
将查询标记为过时以触发重新获取:
tsx
const queryClient = useQueryClient();

// 使所有todos查询失效
queryClient.invalidateQueries({ queryKey: ['todos'] });

// 使特定查询失效
queryClient.invalidateQueries({ queryKey: ['todo', todoId] });

// 使查询失效并立即重新获取
queryClient.invalidateQueries({
  queryKey: ['todos'],
  refetchType: 'active' // 仅重新获取活跃的查询
});

Mutations

变更操作(Mutations)

Mutations are used for creating, updating, or deleting data:
tsx
const mutation = useMutation({
  mutationFn: (newTodo) => {
    return fetch('/api/todos', {
      method: 'POST',
      body: JSON.stringify(newTodo),
    });
  },
  onSuccess: (data, variables, context) => {
    console.log('Success!', data);
  },
  onError: (error, variables, context) => {
    console.error('Error:', error);
  },
  onSettled: (data, error, variables, context) => {
    console.log('Mutation finished');
  },
});

// Trigger mutation
mutation.mutate({ title: 'New Todo' });

// With async/await
mutation.mutateAsync({ title: 'New Todo' })
  .then(data => console.log(data))
  .catch(error => console.error(error));
变更操作用于创建、更新或删除数据:
tsx
const mutation = useMutation({
  mutationFn: (newTodo) => {
    return fetch('/api/todos', {
      method: 'POST',
      body: JSON.stringify(newTodo),
    });
  },
  onSuccess: (data, variables, context) => {
    console.log('成功!', data);
  },
  onError: (error, variables, context) => {
    console.error('错误:', error);
  },
  onSettled: (data, error, variables, context) => {
    console.log('变更操作完成');
  },
});

// 触发变更
mutation.mutate({ title: 'New Todo' });

// 使用async/await
mutation.mutateAsync({ title: 'New Todo' })
  .then(data => console.log(data))
  .catch(error => console.error(error));

React Suspense Integration

React Suspense集成

TanStack Query supports React Suspense with dedicated hooks:
tsx
import { useSuspenseQuery } from '@tanstack/react-query';

function TodoList() {
  // This will suspend the component until data is ready
  const { data } = useSuspenseQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
  });

  // No need for loading states - handled by Suspense boundary
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

// In parent component
function App() {
  return (
    <Suspense fallback={<div>Loading todos...</div>}>
      <TodoList />
    </Suspense>
  );
}
TanStack Query通过专用钩子支持React Suspense:
tsx
import { useSuspenseQuery } from '@tanstack/react-query';

function TodoList() {
  // 组件会挂起直到数据准备完成
  const { data } = useSuspenseQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
  });

  // 无需处理加载状态 - 由Suspense边界处理
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

// 在父组件中
function App() {
  return (
    <Suspense fallback={<div>加载待办事项...</div>}>
      <TodoList />
    </Suspense>
  );
}

Advanced Topics

高级主题

For detailed information on advanced patterns, see the reference files:
如需了解高级模式的详细信息,请参考参考文档:

Infinite Queries

无限查询

For implementing infinite scroll and load-more patterns:
  • See
    references/infinite-queries.md
    for comprehensive guide
  • Covers
    useInfiniteQuery
    hook
  • Bidirectional pagination
  • getNextPageParam
    and
    getPreviousPageParam
  • Refetching and background updates
用于实现无限滚动和加载更多模式:
  • 查看
    references/infinite-queries.md
    获取完整指南
  • 涵盖
    useInfiniteQuery
    钩子
  • 双向分页
  • getNextPageParam
    getPreviousPageParam
  • 重新获取与后台更新

Optimistic Updates

乐观更新

For updating UI before server confirmation:
  • See
    references/optimistic-updates.md
    for detailed patterns
  • Optimistic mutations
  • Rollback on error
  • Context for cancellation
  • UI feedback strategies
在服务器确认前更新UI:
  • 查看
    references/optimistic-updates.md
    获取详细模式
  • 乐观变更
  • 错误时回滚
  • 取消操作的上下文
  • UI反馈策略

TypeScript Support

TypeScript支持

For full type safety and inference:
  • See
    references/typescript.md
    for complete TypeScript guide
  • Type inference from query functions
  • Generic type parameters
  • Typing query options
  • Custom hooks with types
  • Error type narrowing
实现完整的类型安全与类型推断:
  • 查看
    references/typescript.md
    获取完整TypeScript指南
  • 从查询函数进行类型推断
  • 泛型类型参数
  • 为查询选项添加类型
  • 带类型的自定义钩子
  • 错误类型收窄

Query Invalidation Patterns

查询失效模式

For advanced cache invalidation strategies:
  • See
    references/query-invalidation.md
  • Partial matching
  • Predicate functions
  • Refetch strategies
  • Query filters
高级缓存失效策略:
  • 查看
    references/query-invalidation.md
  • 部分匹配
  • 谓词函数
  • 重新获取策略
  • 查询过滤器

Performance Optimization

性能优化

For optimizing query performance:
  • See
    references/performance.md
  • Query deduplication
  • Structural sharing
  • Memory management
  • Query splitting strategies
优化查询性能:
  • 查看
    references/performance.md
  • 查询去重
  • 结构共享
  • 内存管理
  • 查询拆分策略

DevTools

开发工具(DevTools)

TanStack Query DevTools provide visual insights into query state:
bash
npm install @tanstack/react-query-devtools
tsx
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}
DevTools features:
  • View all queries and their states
  • Inspect query data and errors
  • Manually trigger refetches
  • Invalidate queries
  • Monitor cache lifecycle
TanStack Query DevTools提供查询状态的可视化洞察:
bash
npm install @tanstack/react-query-devtools
tsx
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}
DevTools功能:
  • 查看所有查询及其状态
  • 检查查询数据与错误
  • 手动触发重新获取
  • 使查询失效
  • 监控缓存生命周期

Common Patterns

常见模式

Dependent Queries

依赖查询

Run queries in sequence when one depends on another:
tsx
// First query
const { data: user } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
});

// Second query depends on first
const { data: projects } = useQuery({
  queryKey: ['projects', user?.id],
  queryFn: () => fetchProjects(user.id),
  enabled: !!user?.id, // Only run when user.id is available
});
当一个查询依赖另一个查询时,按顺序执行:
tsx
// 第一个查询
const { data: user } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
});

// 第二个查询依赖第一个查询的结果
const { data: projects } = useQuery({
  queryKey: ['projects', user?.id],
  queryFn: () => fetchProjects(user.id),
  enabled: !!user?.id, // 仅当user.id可用时执行
});

Parallel Queries

并行查询

Multiple independent queries in one component:
tsx
function Dashboard() {
  const users = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
  const posts = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });
  const stats = useQuery({ queryKey: ['stats'], queryFn: fetchStats });

  if (users.isLoading || posts.isLoading || stats.isLoading) {
    return <div>Loading...</div>;
  }

  // All queries succeeded
  return <DashboardView users={users.data} posts={posts.data} stats={stats.data} />;
}
在一个组件中执行多个独立查询:
tsx
function Dashboard() {
  const users = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
  const posts = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });
  const stats = useQuery({ queryKey: ['stats'], queryFn: fetchStats });

  if (users.isLoading || posts.isLoading || stats.isLoading) {
    return <div>加载中...</div>;
  }

  // 所有查询成功
  return <DashboardView users={users.data} posts={posts.data} stats={stats.data} />;
}

Dynamic Parallel Queries

动态并行查询

Use
useQueries
for dynamic number of queries:
tsx
import { useQueries } from '@tanstack/react-query';

function TodoLists({ listIds }) {
  const results = useQueries({
    queries: listIds.map((id) => ({
      queryKey: ['list', id],
      queryFn: () => fetchList(id),
    })),
  });

  const isLoading = results.some(result => result.isLoading);
  const data = results.map(result => result.data);

  return <Lists data={data} />;
}
使用
useQueries
处理动态数量的查询:
tsx
import { useQueries } from '@tanstack/react-query';

function TodoLists({ listIds }) {
  const results = useQueries({
    queries: listIds.map((id) => ({
      queryKey: ['list', id],
      queryFn: () => fetchList(id),
    })),
  });

  const isLoading = results.some(result => result.isLoading);
  const data = results.map(result => result.data);

  return <Lists data={data} />;
}

Prefetching

预获取

Prefetch data before it's needed:
tsx
const queryClient = useQueryClient();

// Prefetch on hover
function TodoListLink({ id }) {
  const prefetch = () => {
    queryClient.prefetchQuery({
      queryKey: ['todo', id],
      queryFn: () => fetchTodo(id),
      staleTime: 1000 * 60 * 5, // Cache for 5 minutes
    });
  };

  return (
    <Link to={`/todo/${id}`} onMouseEnter={prefetch}>
      View Todo
    </Link>
  );
}
在需要数据之前提前获取:
tsx
const queryClient = useQueryClient();

// 鼠标悬停时预获取
function TodoListLink({ id }) {
  const prefetch = () => {
    queryClient.prefetchQuery({
      queryKey: ['todo', id],
      queryFn: () => fetchTodo(id),
      staleTime: 1000 * 60 * 5, // 缓存5分钟
    });
  };

  return (
    <Link to={`/todo/${id}`} onMouseEnter={prefetch}>
      查看待办事项
    </Link>
  );
}

Initial Data

初始数据

Provide initial data to avoid loading states:
tsx
function TodoDetail({ todoId, initialTodo }) {
  const { data } = useQuery({
    queryKey: ['todo', todoId],
    queryFn: () => fetchTodo(todoId),
    initialData: initialTodo, // Use this data immediately
    staleTime: 1000 * 60, // Consider fresh for 1 minute
  });

  return <div>{data.title}</div>;
}
提供初始数据以避免加载状态:
tsx
function TodoDetail({ todoId, initialTodo }) {
  const { data } = useQuery({
    queryKey: ['todo', todoId],
    queryFn: () => fetchTodo(todoId),
    initialData: initialTodo, // 立即使用该数据
    staleTime: 1000 * 60, // 1分钟内视为新鲜数据
  });

  return <div>{data.title}</div>;
}

Placeholder Data

占位数据

Show placeholder while loading:
tsx
const { data, isPlaceholderData } = useQuery({
  queryKey: ['todos', page],
  queryFn: () => fetchTodos(page),
  placeholderData: (previousData) => previousData, // Keep previous data while loading
});

// Or use static placeholder
const { data } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  placeholderData: { items: [], total: 0 },
});
加载过程中显示占位内容:
tsx
const { data, isPlaceholderData } = useQuery({
  queryKey: ['todos', page],
  queryFn: () => fetchTodos(page),
  placeholderData: (previousData) => previousData, // 加载时保留之前的数据
});

// 或使用静态占位数据
const { data } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  placeholderData: { items: [], total: 0 },
});

Error Handling

错误处理

Query Errors

查询错误

tsx
const { error, isError } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  retry: 3,
  retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});

if (isError) {
  return <div>Error: {error.message}</div>;
}
tsx
const { error, isError } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  retry: 3,
  retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});

if (isError) {
  return <div>错误:{error.message}</div>;
}

Global Error Handling

全局错误处理

tsx
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      onError: (error) => {
        console.error('Query error:', error);
        // Show toast notification, etc.
      },
    },
    mutations: {
      onError: (error) => {
        console.error('Mutation error:', error);
      },
    },
  },
});
tsx
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      onError: (error) => {
        console.error('查询错误:', error);
        // 显示提示通知等
      },
    },
    mutations: {
      onError: (error) => {
        console.error('变更错误:', error);
      },
    },
  },
});

Error Boundaries

错误边界

Combine with React Error Boundaries:
tsx
import { useQuery } from '@tanstack/react-query';
import { ErrorBoundary } from 'react-error-boundary';

function TodoList() {
  const { data } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
    throwOnError: true, // Throw errors to error boundary
  });

  return <div>{/* render data */}</div>;
}

function App() {
  return (
    <ErrorBoundary fallback={<div>Something went wrong</div>}>
      <TodoList />
    </ErrorBoundary>
  );
}
与React错误边界结合使用:
tsx
import { useQuery } from '@tanstack/react-query';
import { ErrorBoundary } from 'react-error-boundary';

function TodoList() {
  const { data } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
    throwOnError: true, // 将错误抛给错误边界
  });

  return <div>{/* 渲染数据 */}</div>;
}

function App() {
  return (
    <ErrorBoundary fallback={<div>出现错误</div>}>
      <TodoList />
    </ErrorBoundary>
  );
}

Best Practices

最佳实践

  1. Use Query Keys Wisely
    • Structure keys hierarchically:
      ['todos', 'list', { filters }]
    • Include all variables in the key
    • Keep keys consistent across your app
  2. Set Appropriate staleTime
    • Static data:
      staleTime: Infinity
    • Frequently changing:
      staleTime: 0
      (default)
    • Moderately changing:
      staleTime: 1000 * 60 * 5
      (5 minutes)
  3. Handle Loading and Error States
    • Always check
      isLoading
      and
      error
    • Provide meaningful loading indicators
    • Show user-friendly error messages
  4. Optimize Refetching
    • Disable unnecessary refetches with
      refetchOnWindowFocus: false
    • Use
      staleTime
      to reduce refetches
    • Consider using
      refetchInterval
      for polling
  5. Invalidate Efficiently
    • Invalidate specific queries, not all queries
    • Use query key prefixes for related queries
    • Combine with optimistic updates for better UX
  6. Use TypeScript
    • Type your query functions for type inference
    • Use generic type parameters when needed
    • Enable strict type checking
  7. Leverage DevTools
    • Install DevTools in development
    • Monitor query behavior
    • Debug cache issues
  1. 合理使用查询键
    • 采用层级结构:
      ['todos', 'list', { filters }]
    • 将所有变量包含在查询键中
    • 在应用中保持查询键的一致性
  2. 设置合适的staleTime
    • 静态数据:
      staleTime: Infinity
    • 频繁变化的数据:
      staleTime: 0
      (默认值)
    • 中等频率变化的数据:
      staleTime: 1000 * 60 * 5
      (5分钟)
  3. 处理加载与错误状态
    • 始终检查
      isLoading
      error
      状态
    • 提供有意义的加载指示器
    • 显示用户友好的错误信息
  4. 优化重新获取策略
    • 通过
      refetchOnWindowFocus: false
      禁用不必要的重新获取
    • 使用
      staleTime
      减少重新获取次数
    • 考虑使用
      refetchInterval
      进行轮询
  5. 高效失效查询
    • 使特定查询失效,而非所有查询
    • 为相关查询使用查询键前缀
    • 结合乐观更新以提升用户体验
  6. 使用TypeScript
    • 为查询函数添加类型以实现类型推断
    • 必要时使用泛型类型参数
    • 启用严格类型检查
  7. 利用开发工具
    • 在开发环境中安装DevTools
    • 监控查询行为
    • 调试缓存问题

Resources

资源

Migration from v4

从v4迁移

If you're upgrading from React Query v4:
如果你正在从React Query v4升级: