目录
- 什么是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>
<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