RTK Query 详解
RTK Query 是 Redux Toolkit 内置的数据请求与缓存管理工具,专为解决 Redux 项目中数据获取、缓存同步、状态维护等痛点设计。它基于 Redux 生态,无需手动编写 action、reducer 和 selector 来处理异步请求逻辑,大幅简化了数据交互流程。
一、核心定位与优势
1. 核心定位
RTK Query 不是独立的请求库,而是基于 Redux 的数据管理层,底层依赖 fetch 或 axios 等请求工具,专注于请求状态管理、数据缓存、自动重请求等上层能力。
2. 核心优势
- 减少样板代码:无需手动定义请求相关的 action type、action creator、reducer。
- 自动缓存与失效:请求结果自动缓存,支持配置缓存时间、失效策略,避免重复请求。
- 请求状态管理:内置
loading/error/success等状态,无需手动维护。 - 数据预取与更新:支持组件挂载前预加载数据,以及主动触发数据重请求。
- 与 Redux 无缝集成:数据存储在 Redux Store 中,可通过
useSelector访问,也支持与其他 Redux 中间件配合使用。
二、核心概念
1. API Slice
createApi 是 RTK Query 的核心入口,用于创建API Slice,定义一组相关的请求接口配置。
- 每个 API Slice 对应一个业务模块(如用户模块、商品模块)。
- 配置项包括请求基础路径、请求拦截器、响应转换器、缓存策略等。
2. Endpoint
Endpoint 是 API Slice 中定义的单个请求接口,对应一个具体的 API 端点(如 GET /users、POST /products)。
- 分为两种类型:
- 查询(Query):用于获取数据的请求(如 GET),默认开启缓存。
- 突变(Mutation):用于修改数据的请求(如 POST/PUT/DELETE),默认不缓存,触发后通常需要更新缓存。
3. Hooks 自动生成
RTK Query 会根据定义的 Endpoint 自动生成 React Hooks,供组件直接调用,无需手动封装。
- Query 类型的 Endpoint 生成
useXXXQuery钩子。 - Mutation 类型的 Endpoint 生成
useXXXMutation钩子。
4. 缓存与标签(Tags)
标签(Tags) 是 RTK Query 实现缓存更新的核心机制,用于标记缓存数据的所属类别。
- 给 Query 请求的响应数据打上标签(如
["User"])。 - 当 Mutation 请求修改数据后,通过
invalidatesTags使对应标签的缓存失效,触发 Query 自动重请求,保证数据一致性。
三、基本使用流程
以 React 项目为例,完整使用 RTK Query 分为 4 步:
步骤 1:安装依赖
确保已安装 @reduxjs/toolkit 和 react-redux:
npm install @reduxjs/toolkit react-redux
步骤 2:创建 API Slice
使用 createApi 定义请求配置,以用户模块为例:
// src/services/userApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import type { User } from '../types';
// 定义 API Slice
export const userApi = createApi({
// 必选:缓存 reducer 的唯一标识,会挂载到 Redux Store 的该路径下
reducerPath: 'userApi',
// 必选:基础请求函数,基于 fetch 封装,支持配置 baseUrl、请求头
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.example.com' }),
// 可选:定义标签类型,用于缓存管理
tagTypes: ['User'],
// 必选:定义 Endpoint 集合
endpoints: (builder) => ({
// 1. Query Endpoint:获取用户列表
getUsers: builder.query<User[], void>({
query: () => '/users', // 请求路径
providesTags: ['User'], // 给响应数据打标签
}),
// 2. Query Endpoint:根据 ID 获取单个用户
getUserById: builder.query<User, number>({
query: (id) => `/users/${id}`,
providesTags: (result, error, id) => [{ type: 'User', id }], // 动态打标签(带 ID)
}),
// 3. Mutation Endpoint:添加用户
addUser: builder.mutation<User, Partial<User>>({
query: (newUser) => ({
url: '/users',
method: 'POST',
body: newUser,
}),
invalidatesTags: ['User'], // 使 User 标签的缓存失效
}),
}),
});
// 自动生成 Hooks,命名规则:use + Endpoint 名 + Query/Mutation
export const { useGetUsersQuery, useGetUserByIdQuery, useAddUserMutation } = userApi;
步骤 3:配置 Redux Store
将 API Slice 的 reducer 和中间件添加到 Store:
// src/store.ts
import { configureStore } from '@reduxjs/toolkit';
import { userApi } from './services/userApi';
export const store = configureStore({
reducer: {
// 挂载 API Slice 的 reducer
[userApi.reducerPath]: userApi.reducer,
},
// 添加 RTK Query 的中间件,用于处理缓存、重请求等逻辑
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(userApi.middleware),
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
步骤 4:在 React 组件中使用 Hooks
直接调用自动生成的 Hooks 发起请求,获取数据和状态:
// src/components/Users.tsx
import React from 'react';
import { useGetUsersQuery, useAddUserMutation } from '../services/userApi';
const Users = () => {
// 调用 Query Hook,发起请求
// 参数:query 的入参(这里 void 类型传 undefined)
// 返回值:data(响应数据)、isLoading(加载中)、error(错误信息)等
const { data: users, isLoading, error } = useGetUsersQuery(undefined);
// 调用 Mutation Hook,返回 [触发函数, 状态对象]
const [addUser, { isLoading: isAdding }] = useAddUserMutation();
const handleAddUser = async () => {
try {
await addUser({ name: 'New User', age: 25 }).unwrap();
alert('添加成功');
} catch (err) {
alert('添加失败');
}
};
if (isLoading) return <div>加载中...</div>;
if (error) return <div>请求失败</div>;
return (
<div>
<h2>用户列表</h2>
<button onClick={handleAddUser} disabled={isAdding}>
{isAdding ? '添加中...' : '添加用户'}
</button>
<ul>
{users?.map((user) => (
<li key={user.id}>{user.name} - {user.age} 岁</li>
))}
</ul>
</div>
);
};
export default Users;
四、关键特性详解
1. 缓存控制
(1)缓存有效期
通过 keepUnusedDataFor 配置单个 Query 的缓存时间(单位:秒),默认 60 秒:
getUsers: builder.query<User[], void>({
query: () => '/users',
providesTags: ['User'],
keepUnusedDataFor: 30, // 30 秒内无组件使用则清除缓存
}),
(2)标签缓存策略
标签分为静态标签和动态标签,用于精准控制缓存失效范围:
- 静态标签:适用于列表数据,如
providesTags: ['User'],失效时整个列表缓存更新。 - 动态标签:适用于单条数据,如
providesTags: (result, error, id) => [{ type: 'User', id }],失效时仅更新对应 ID 的数据缓存。
示例:修改用户后,仅失效该用户的缓存
updateUser: builder.mutation<User, { id: number; data: Partial<User> }>({
query: ({ id, data }) => ({
url: `/users/${id}`,
method: 'PUT',
body: data,
}),
// 仅使 ID 对应的 User 标签失效
invalidatesTags: (result, error, { id }) => [{ type: 'User', id }],
}),
2. 预加载数据
使用 initiate 方法在组件挂载前预加载数据,适用于路由跳转前的预请求:
// src/App.tsx
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { userApi } from './services/userApi';
const App = () => {
const dispatch = useDispatch();
useEffect(() => {
// 预加载用户列表数据
const promise = dispatch(userApi.endpoints.getUsers.initiate(undefined));
// 组件卸载时取消请求(可选)
return () => promise.unsubscribe();
}, [dispatch]);
return <Users />;
};
3. 自定义请求逻辑
(1)请求拦截与响应转换
通过 baseQuery 的 prepareHeaders 配置请求头(如 Token),通过 transformResponse 转换响应数据:
baseQuery: fetchBaseQuery({
baseUrl: 'https://api.example.com',
prepareHeaders: (headers, { getState }) => {
// 从 Redux Store 获取 Token
const token = (getState() as RootState).auth.token;
if (token) {
headers.set('authorization', `Bearer ${token}`);
}
return headers;
},
}),
endpoints: (builder) => ({
getUsers: builder.query<User[], void>({
query: () => '/users',
// 转换响应数据,提取 data 字段
transformResponse: (response: { data: User[] }) => response.data,
}),
}),
(2)错误处理
通过 transformErrorResponse 统一处理错误响应:
getUsers: builder.query<User[], void>({
query: () => '/users',
transformErrorResponse: (response) => {
// 自定义错误信息
return { message: response.data?.msg || '请求失败' };
},
}),
4. 与 TypeScript 结合
RTK Query 对 TypeScript 有完善的类型支持,只需定义好请求参数和响应数据的类型,即可获得完整的类型提示:
- 定义
builder.query<TResult, TArg>:TResult为响应数据类型,TArg为请求参数类型。 - 自动生成的 Hooks 会继承 Endpoint 的类型,避免类型错误。
五、与传统 Redux 异步方案的对比
| 特性 | 传统 Redux(thunk + 手动 reducer) | RTK Query |
|---|---|---|
| 样板代码量 | 多(需定义 action、reducer 等) | 少(仅需定义 Endpoint) |
| 缓存管理 | 需手动实现 | 内置自动缓存、失效策略 |
| 请求状态(loading) | 需手动维护 | 内置 isLoading/isError 等状态 |
| 数据一致性 | 需手动同步 | 基于标签自动更新缓存 |
| 代码复用性 | 低(需封装重复逻辑) | 高(Endpoint 可跨组件复用) |
六、适用场景与注意事项
1. 适用场景
- 中大型 React + Redux 项目,需要管理大量异步请求。
- 需要缓存数据、避免重复请求的场景(如列表页、详情页)。
- 需要统一管理请求状态、简化错误处理的场景。
2. 注意事项
- RTK Query 依赖 Redux,小型项目(如仅需少量请求)可直接使用
fetch或axios。 - 缓存策略需合理配置,避免因缓存过期导致数据不一致。
- Mutation 请求触发后,需正确配置
invalidatesTags,否则缓存不会自动更新。