为什么很多大项目都在用 @tanstack/react-query?它解决了什么问题
如果你在做 react 项目,并且需要从后端获取数据,那你很可能遇到过这些情况:
同样的数据在不同组件里重复请求
用户操作后,界面数据没有及时更新
加载状态和错误状态需要自己处理
分页、无限滚动等功能实现起来很麻烦
@tanstack/react-query(原名 React Query)就是专门为解决这些问题而生的。它是一个专门管理服务器状态的数据获取库。
它能帮你做什么
简单来说,react-query 帮你处理所有跟后台数据相关的工作:
自动缓存数据,避免重复请求
数据过期后自动重新获取
错误自动重试
乐观更新(先更新界面,再发送请求)
分页和无限滚动支持
你不再需要把这些逻辑写在 Redux 或者 Context 里,代码会简洁很多。
开始使用
安装:
pnpm add @tanstack/react-query在应用最外层设置:
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import Layout from '@/Layout';
export default function App() {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<Layout />
</QueryClientProvider>
);
}这样你的所有组件就都能使用 react-query 的功能了。
获取数据:useQuery
看一个获取用户信息的例子:
import React from 'react';
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
// 定义获取数据的函数
function fetchUser(userId) {
return axios.get(`https://jsonplaceholder.typicode.com/users/${userId}`)
.then(res => res.data);
}
function User({ userId }) {
const { data, isLoading, isError, error, refetch } = useQuery({
queryKey: ['user', userId], // 缓存的键名
queryFn: () => fetchUser(userId), // 获取数据的函数
staleTime: 1000 * 60, // 1分钟内数据是新鲜的,不会重新请求
cacheTime: 1000 * 60 * 5, // 数据缓存5分钟
retry: 2, // 失败自动重试2次
});
if (isLoading) return <div>加载中...</div>;
if (isError) return <div>出错了: {error.message}</div>;
return (
<div>
<h2>{data.name}</h2>
<p>邮箱: {data.email}</p>
<button onClick={() => refetch()}>重新加载</button>
</div>
);
}这里有几个重要的参数:
queryKey:数据的唯一标识,相同 key 的数据会被缓存和复用
staleTime:数据保鲜时间,这段时间内不会重新请求
cacheTime:数据缓存时间,即使组件卸载了数据还会保留
修改数据:useMutation
当你需要添加、修改或删除数据时,可以用 useMutation:
import React, { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
// 获取待办列表
const fetchTodos = () => axios.get('/api/todos').then(res => res.data);
// 添加新待办
const addTodo = (text) => axios.post('/api/todos', { text }).then(res => res.data);
export function Todos() {
const queryClient = useQueryClient();
const [newText, setNewText] = useState('');
// 获取待办列表
const { data: todos = [] } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos
});
// 添加新待办
const mutation = useMutation({
mutationFn: addTodo,
// 乐观更新:先更新界面,再发送请求
onMutate: async (text) => {
// 取消正在进行的请求,避免冲突
await queryClient.cancelQueries({ queryKey: ['todos'] });
// 保存当前数据,用于出错时回滚
const previousTodos = queryClient.getQueryData(['todos']);
// 立即更新缓存
queryClient.setQueryData(['todos'], [...(previousTodos || []), {
id: Date.now(),
text,
// 临时标识,等真实请求成功后会替换
optimistic: true
}]);
return { previousTodos };
},
// 出错时回滚到之前的状态
onError: (err, variables, context) => {
if (context?.previousTodos) {
queryClient.setQueryData(['todos'], context.previousTodos);
}
},
// 无论成功失败,都重新获取最新数据
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] });
}
});
return (
<div>
<h2>待办列表</h2>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
{todo.optimistic && ' (保存中...)'}
</li>
))}
</ul>
<input
value={newText}
onChange={e => setNewText(e.target.value)}
placeholder="输入新待办"
/>
<button onClick={() => mutation.mutate(newText)}>
添加
</button>
</div>
);
}乐观更新让用户体验更好,用户不用等待请求完成就能看到结果。
处理数据依赖关系
有时候你需要先获取一个数据,然后基于这个数据获取另一个数据:
// 先获取用户信息
const userQuery = useQuery({
queryKey: ['userByName', username],
queryFn: () => axios.get(`/api/users?username=${username}`).then(res => res.data),
});
// 等获取到用户ID后,再获取用户的文章
const postsQuery = useQuery({
queryKey: ['posts', userQuery.data?.id],
queryFn: () => axios.get(`/api/users/${userQuery.data.id}/posts`).then(res => res.data),
// 只有拿到用户ID后才执行
enabled: !!userQuery.data?.id,
});enabled 参数让你能够控制什么时候执行请求。
开发工具
react-query 提供了开发工具,让你在开发时能看到缓存的数据、正在进行的请求等信息:
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Layout />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}安装开发工具包:
pnpm add @tanstack/react-query-devtools实际项目中的建议
统一管理查询键名:把所有的 queryKey 放在一个文件里管理,避免拼写错误
封装自定义 Hooks:把 useQuery 和 useMutation 封装成自定义 Hook,方便复用
设置全局默认值:可以配置默认的 staleTime 和 cacheTime
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5分钟
cacheTime: 1000 * 60 * 30, // 30分钟
},
},
});总结
@tanstack/react-query 解决了 React 项目中数据管理的核心问题。它让你的代码更简洁,用户体验更好,开发效率更高。虽然需要一些学习成本,但一旦用起来,你会发现它大大简化了数据获取和状态管理的工作。
如果你的项目中有复杂的数据获取需求,或者你厌倦了自己处理加载状态和缓存逻辑,那么很值得尝试一下 react-query。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!