前端使用 React Query 管理“服务器状态”
目录
- 一、安装注册 react-query
- 二、使用 react-query
- 1、定义获取数据的查询函数 fetchTodos
- 2、使用 useQuery 获取服务器状态
- 3、新增数据(useMutation + 自动刷新列表)
- 4、删除数据(useMutation + 乐观更新)
- 5、组件组合
- 三、React Query 能做什么?不能做什么?
一、安装注册 react-query
首先要安装 @tanstack/react-query:
npm install @tanstack/react-query
然后在入口文件中必须加 QueryClientProvider:
// main.tsx / index.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import App from './App';const queryClient = new QueryClient();export default function Root() {return (<QueryClientProvider client={queryClient}><App /></QueryClientProvider>);
}
二、使用 react-query
使用的 React Query 管理“服务器状态”的步骤:
- 获取数据(useQuery)
- 创建数据(useMutation)
- 更新与删除
- 缓存、重新请求、乐观更新
- 实战项目文件结构
【举个例子】:
假设有一个服务器 API /api/todos。现在需要你实现以下需求:
①、获取 Todo 列表
②、新增 Todo
③、删除 Todo
④、自动刷新、缓存、错误处理
1、定义获取数据的查询函数 fetchTodos
// api/todoApi.ts
export const fetchTodos = async () => {const res = await fetch('/api/todos');if (!res.ok) throw new Error('Failed to fetch todos');return res.json();
};export const addTodo = async (title: string) => {const res = await fetch('/api/todos', {method: 'POST',body: JSON.stringify({ title }),headers: { 'Content-Type': 'application/json' }});if (!res.ok) throw new Error('Failed to add todo');return res.json();
};export const deleteTodo = async (id: number) => {const res = await fetch(`/api/todos/${id}`, {method: 'DELETE'});if (!res.ok) throw new Error('Failed to delete todo');return res.json();
};
2、使用 useQuery 获取服务器状态
import { useQuery } from '@tanstack/react-query';
import { fetchTodos } from './api/todoApi';export function TodoList() {const { data, isLoading, error } = useQuery({queryKey: ['todos'], // 缓存 key(全局唯一)queryFn: fetchTodos, // 请求函数staleTime: 5000, // 5 秒内不重新请求refetchOnWindowFocus: false, // 切回窗口不重新请求});if (isLoading) return <div>加载中...</div>;if (error) return <div>加载失败</div>;return (<ul>{data.map(todo => (<li key={todo.id}>{todo.title}</li>))}</ul>);
}
3、新增数据(useMutation + 自动刷新列表)
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { addTodo } from './api/todoApi';export function AddTodo() {const queryClient = useQueryClient();const mutation = useMutation({mutationFn: (title: string) => addTodo(title),onSuccess() {// 手动刷新 todos 列表queryClient.invalidateQueries(['todos']);},});const handleAdd = () => {mutation.mutate('新的任务');};return (<button onClick={handleAdd} disabled={mutation.isPending}>{mutation.isPending ? '添加中...' : '添加 TODO'}</button>);
}
4、删除数据(useMutation + 乐观更新)
React Query 的强大之处是:可以不用等服务器响应,先更新 UI,失败再回滚:
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { deleteTodo } from './api/todoApi';export function DeleteTodoButton({ id }) {const queryClient = useQueryClient();const mutation = useMutation({mutationFn: () => deleteTodo(id),// 乐观更新onMutate: async () => {await queryClient.cancelQueries(['todos']);const previousTodos = queryClient.getQueryData(['todos']);queryClient.setQueryData(['todos'], (old: any[]) =>old.filter(todo => todo.id !== id));return { previousTodos };},// 失败时回滚onError: (_err, _variables, context) => {queryClient.setQueryData(['todos'], context.previousTodos);},// 成功后真正拉取最新数据onSettled: () => {queryClient.invalidateQueries(['todos']);},});return (<button onClick={() => mutation.mutate()}>删除</button>);
}
5、组件组合
export default function App() {return (<div><h1>Todo 管理(React Query)</h1><AddTodo /><TodoList /></div>);
}
三、React Query 能做什么?不能做什么?
负责管理“服务器状态”的难点:
- 缓存
- 后台刷新
- 自动请求合并
- 乐观更新
- 错误恢复
- 对象引用稳定性
- 重新请求策略
- 全局状态同步
不处理:
- 本地 UI 状态(modal 是否打开?)
- 表单状态
- 跨组件 UI 状态
👉 这些依然由 useState/useReducer/Zustand 来处理。
