npm 周下载量 :超过 170 万次(截至 2023 年底),这个数字在持续快速增长
GitHub Stars :超过 38k
在用的知名公司:Netflix, Facebook, Amazon, Microsoft, Airbnb
🔹 What(它是什么)
React Query 是一个 专门用来管理「服务端数据」的库。
-
React 自身只提供了
useState
、useEffect
等 Hook,但它们适合处理本地 UI 状态(比如按钮是否展开、输入框值)。 -
但是一旦涉及 远程数据(API 请求),就会出现很多问题:加载状态、错误处理、缓存、刷新、并发请求覆盖......这些在传统写法里非常复杂。
-
React Query 就是帮你把这一堆麻烦的逻辑"打包"成几个简单的 Hook,比如:
useQuery
→ 读数据useMutation
→ 写数据(增删改)
🔹 Why(为什么要用它)
传统 useEffect + useState
写法的问题:
- 需要手写 loading / error / data 三套状态,还要同步管理。
- 请求竞态问题:比如快速切换用户详情页面时,旧请求覆盖新请求,页面展示错数据。
- 缓存难管理:离开页面再回来,要不要重新请求?数据是否过时?
- 代码冗长、容易出错,还经常要配合 Redux/Context 去做全局数据管理,复杂度爆炸。
👉 React Query 的价值:
- 状态全自动(loading、error、success 状态都有现成的字段)
- 缓存智能(Stale-While-Revalidate 策略,先给旧数据,再在后台刷新最新数据)
- 请求时机自动化(窗口切回来、网络恢复、参数变化时都会自动刷新)
- 声明式写法(只说"我要这份数据",不用手写各种状态切换逻辑)
🔹 How(怎么用)
-
初始化客户端(全局只需要一次)
jsximport { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient(); function App() { return ( <QueryClientProvider client={queryClient}> <MyComponent /> </QueryClientProvider> ); }
-
获取数据:
useQuery
jsximport { useQuery } from '@tanstack/react-query'; import axios from 'axios'; function Todos() { const { data, isLoading, isError } = useQuery( ['todos'], // Query Key,用来标识这份数据 () => axios.get('/api/todos').then(res => res.data) // Query Fn ); if (isLoading) return <p>加载中...</p>; if (isError) return <p>出错了!</p>; return <ul>{data.map(todo => <li key={todo.id}>{todo.title}</li>)}</ul>; }
-
修改数据:
useMutation
jsximport { useMutation, useQueryClient } from '@tanstack/react-query'; import axios from 'axios'; function AddTodo() { const queryClient = useQueryClient(); const mutation = useMutation( newTodo => axios.post('/api/todos', newTodo), { onSuccess: () => { // 数据变更后,让 todos 的缓存失效,触发重新获取 queryClient.invalidateQueries(['todos']); } } ); return ( <button onClick={() => mutation.mutate({ title: '新任务' })}> 添加任务 </button> ); }
🔹 收获(你能得到什么)
-
开发效率提升:不用再手写一堆样板代码。
-
更好的用户体验:秒开页面(缓存先展示)、后台静默刷新、断网自动恢复。
-
数据永远新鲜:不用担心旧请求覆盖新请求,React Query 会自动帮你管。
-
更清晰的分工:
- 本地 UI 状态(开关、输入框等) → 继续用
useState
或 Redux/Zustand - 服务端数据(API) → 全交给 React Query
- 本地 UI 状态(开关、输入框等) → 继续用
实战
基础实例:获取用户列表
jsx
import { useQuery } from '@tanstack/react-query';
import { getUserList } from './api'; // 你的 API 函数
function UserList() {
// 使用 useQuery Hook
// ['users'] 是查询密钥,唯一标识这个查询
const { data, isLoading, isError, error } = useQuery({
queryKey: ['users'],
queryFn: getUserList, // 一个返回 Promise 的函数,例如:() => fetch('/api/users').then(res => res.json())
});
if (isLoading) return <div>加载中...</div>;
if (isError) return <div>出错啦! {error.message}</div>;
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
带参数查询:获取单个用户详情
这个例子展示了如何依赖一个状态(如 userId
)来发起查询。
jsx
function UserProfile({ userId }) {
// 查询密钥包含了 userId,当 userId 变化时,会自动发起新的请求
const { data: user, isLoading } = useQuery({
queryKey: ['user', userId],
queryFn: () => getUserById(userId),
enabled: !!userId, // 只有 userId 存在时才启用查询
});
if (isLoading) return <div>加载用户信息...</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
修改数据实例:创建新用户
这个例子展示了 useMutation
的典型用法,包括成功后使旧缓存失效。
jsx
import { useMutation, useQueryClient } from '@tanstack/react-query';
function AddUser() {
const queryClient = useQueryClient();
const [name, setName] = useState('');
// 定义 Mutation
const createUserMutation = useMutation({
mutationFn: (newUser) => createUserApi(newUser), // API 函数:POST /api/users
onSuccess: () => {
// ✅ 创建成功后,作废并重新获取 'users' 列表的查询
queryClient.invalidateQueries({ queryKey: ['users'] });
setName(''); // 清空输入框
alert('用户创建成功!');
},
onError: (error) => {
alert(`创建失败: ${error.message}`);
}
});
const handleSubmit = (e) => {
e.preventDefault();
createUserMutation.mutate({ name: name }); // 触发 Mutation
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="输入用户名"
/>
<button type="submit" disabled={createUserMutation.isLoading}>
{createUserMutation.isLoading ? '创建中...' : '创建用户'}
</button>
</form>
);
}
👉 总结一句: React Query 让你从「手动管理请求+状态的痛苦」中解放出来,把精力专注在业务逻辑和用户体验上。