TanStack React Query 完全指南:从0到精通

发布于:2025-08-06 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

  1. 什么是React Query
  2. 安装和基础设置
  3. 基础概念
  4. useQuery - 数据获取
  5. useMutation - 数据变更
  6. 查询键(Query Keys)
  7. 缓存和数据同步
  8. 错误处理
  9. 加载状态管理
  10. 分页和无限查询
  11. 乐观更新
  12. 查询失效和重新获取
  13. 高级模式
  14. 性能优化
  15. 实战项目示例

什么是React Query?

TanStack React Query 是一个强大的数据同步库,专门用于在React应用中管理服务器状态。它解决了以下问题:

  • 缓存管理
  • 后台数据同步
  • 陈旧数据更新
  • 分页和懒加载
  • 内存和垃圾收集优化
  • 查询去重

安装和基础设置

1. 安装依赖

npm install @tanstack/react-query
# 或
yarn add @tanstack/react-query

2. 基础设置

// App.js
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

// 创建QueryClient实例
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5分钟
      cacheTime: 1000 * 60 * 10, // 10分钟
      retry: 3, // 失败重试3次
      refetchOnWindowFocus: false, // 窗口聚焦时不自动重新获取
    },
  },
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <div className="App">
        <MyComponent />
      </div>
      {/* 开发工具,生产环境会自动隐藏 */}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

export default App;

基础概念

重要术语

  • Query: 数据获取操作,通常是GET请求
  • Mutation: 数据变更操作,如POST、PUT、DELETE
  • Query Key: 查询的唯一标识符
  • Cache: React Query内部的数据缓存
  • Stale Time: 数据被认为是"新鲜"的时间
  • Cache Time: 数据在缓存中保存的时间

useQuery - 数据获取

1. 基础使用

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

// 定义数据获取函数
const fetchUsers = async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  if (!response.ok) {
    throw new Error('获取用户失败');
  }
  return response.json();
};

function UsersList() {
  const {
    data,           // 查询返回的数据
    isLoading,      // 首次加载状态
    isFetching,     // 任何时候的获取状态
    error,          // 错误信息
    isError,        // 是否有错误
    isSuccess,      // 是否成功
    refetch,        // 手动重新获取函数
  } = useQuery({
    queryKey: ['users'],           // 查询键
    queryFn: fetchUsers,           // 查询函数
    staleTime: 1000 * 60 * 5,     // 5分钟内数据不会过期
    cacheTime: 1000 * 60 * 10,    // 缓存10分钟
  });

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

  return (
    <div>
      <h2>用户列表</h2>
      <button onClick={() => refetch()}>刷新</button>
      <ul>
        {data?.map(user => (
          <li key={user.id}>{user.name} - {user.email}</li>
        ))}
      </ul>
    </div>
  );
}

2. 带参数的查询

const fetchUserById = async (userId) => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
  if (!response.ok) {
    throw new Error('获取用户详情失败');
  }
  return response.json();
};

function UserDetail({ userId }) {
  const { data: user, isLoading, error } = useQuery({
    queryKey: ['user', userId],        // 包含参数的查询键
    queryFn: () => fetchUserById(userId),
    enabled: !!userId,                 // 只有当userId存在时才执行查询
  });

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

  return (
    <div>
      <h3>{user?.name}</h3>
      <p>邮箱: {user?.email}</p>
      <p>电话: {user?.phone}</p>
      <p>网站: {user?.website}</p>
    </div>
  );
}

3. 条件查询

function SearchUsers() {
  const [searchTerm, setSearchTerm] = useState('');

  const { data, isLoading, isFetching } = useQuery({
    queryKey: ['users', 'search', searchTerm],
    queryFn: () => searchUsers(searchTerm),
    enabled: searchTerm.length >= 3,        // 至少3个字符才搜索
    keepPreviousData: true,                 // 保持之前的数据,避免闪烁
  });

  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="搜索用户 (至少3个字符)"
      />
      {isFetching && <div>搜索中...</div>}
      {/* 渲染搜索结果 */}
    </div>
  );
}

useMutation - 数据变更

1. 基础变更操作

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

const createUser = async (userData) => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(userData),
  });
  
  if (!response.ok) {
    throw new Error('创建用户失败');
  }
  
  return response.json();
};

function CreateUserForm() {
  const queryClient = useQueryClient();
  
  const mutation = useMutation({
    mutationFn: createUser,
    onSuccess: (data) => {
      // 成功后使相关查询失效,触发重新获取
      queryClient.invalidateQueries({ queryKey: ['users'] });
      alert('用户创建成功!');
    },
    onError: (error) => {
      alert(`创建失败: ${error.message}`);
    },
  });

  const handleSubmit = (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const userData = {
      name: formData.get('name'),
      email: formData.get('email'),
    };
    
    mutation.mutate(userData);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="姓名" required />
      <input name="email" type="email" placeholder="邮箱" required />
      <button 
        type="submit" 
        disabled={mutation.isPending}
      >
        {mutation.isPending ? '创建中...' : '创建用户'}
      </button>
      
      {mutation.error && (
        <div style={
  
  { color: 'red' }}>
          错误: {mutation.error.message}
        </div>
      )}
    </form>
  );
}

2. 更新和删除操作

const updateUser = async ({ id, userData }) => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/j

网站公告

今日签到

点亮在社区的每一天
去签到