TanStack React Query 完全指南:从0到精通
目录
- 什么是React Query
- 安装和基础设置
- 基础概念
- useQuery - 数据获取
- useMutation - 数据变更
- 查询键(Query Keys)
- 缓存和数据同步
- 错误处理
- 加载状态管理
- 分页和无限查询
- 乐观更新
- 查询失效和重新获取
- 高级模式
- 性能优化
- 实战项目示例
什么是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><inputtype="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