使用RTK查询发送数据:突变处理实用指南
了解如何在 RTK Query 中使用 builder.mutation() 来发送 POST 请求、处理加载与错误状态,以及构建可用的表单组件。
简介
之前我们介绍了如何设置 Redux Toolkit Query(RTK Query)以及使用 builder.query() 执行读取操作。这样我们就可以从 API 获取数据并在 React 应用中展示,而无需手动编写 useEffect()、useState() 或自定义的获取数据逻辑。
不过,在处理 API 时,仅读取数据还只是其中一半。大多数应用还需要能够在服务器端添加、修改或删除记录的功能。
以下是几种常见场景:
- 注册表单提交新的账户信息。
- 登录页面发送凭证以供验证。
- 博客平台发布新文章。
- 在线商店下新订单。
- 待办事项应用保存新添加的任务。
这些操作都会将信息从客户端发送到服务器,通常是通过HTTP POST请求来实现的。
RTK Query并不将POST调用视为查询操作——而是将其视为变更操作。
本文将详细介绍如何使用builder.mutation()发送数据。我们会逐行解析代码及各项配置设置,帮助您不仅了解需要输入什么内容,还能理解每部分的作用。
了解HTTP方法
在深入研究代码之前,先了解一下不同的HTTP动词及其用途会很有帮助。
典型的REST API会提供多种操作:
| 方法 | 用途 | 示例 |
|---|---|---|
| GET | 读取数据 | 获取所有用户 |
| POST | 添加新数据 |
本指南重点介绍POST,即用于在服务器上创建新资源的方法。
为何POST不使用builder.query()?
这是初学者常感困惑的问题。
如果builder.query()能够获取数据,为何同样的函数不能将数据发送到服务器?
原因在于每种工具的设计用途不同。
查询
查询的存在目的是获取信息。
典型示例:
- 获取用户
- 获取产品
- 获取订单
- 获取帖子
由于相同的数据可能会被反复请求,查询会自动缓存其结果。
变更操作
变更操作的目的是修改数据。
典型示例包括:
- 创建用户
- 更新用户
- 删除用户
- 登录
- 注册
变更操作会向服务器发出信号,表明有内容需要更改。
正是基于这种区别,RTK Query才会将变更操作与普通查询分开处理。
我们要构建的内容
我们将创建一个表单,用于向服务器提交新用户信息。
目标端点为:
https://jsonplaceholder.typicode.com/users
而请求体中发送的负载数据则如下所示:
{
"name": "John Doe",
"email": "john@example.com"
}
项目结构
src
│
├── app
│ └── store.js
│
├── services
│ └── api.js
│
├── components
│ └── AddUser.jsx
│
├── App.jsx
│
└── main.jsx
Redux 存储的配置保持不变。我们只需添加一个变异端点以及一个处理表单提交的组件。
第一步 — 创建变异端点
打开 API 服务文件:
src/services/api.js
然后在 endpoints 对象中添加新的端点定义。
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const api = createApi({
reducerPath: "api", baseQuery: fetchBaseQuery({
baseUrl: "https://jsonplaceholder.typicode.com/",
}), endpoints: (builder) => ({ addUser: builder.mutation({ query: (newUser) => ({
url: "users",
method: "POST",
body: newUser,
}), }), }),});export const {
useAddUserMutation,
} = api;
让我们逐行来分析。
理解 builder.mutation()
addUser: builder.mutation({
虽然 builder.query() 用于获取数据,但每当需要在服务器上修改某些内容时,就应该使用 builder.mutation()。
常见的应用场景包括:
- 创建用户
- 注册账户
- 登录
- 更新产品信息
- 删除帖子
每当您的应用在后端写入或修改数据时,mutation都是合适的工具。
了解 query()
query: (newUser) => ({
该函数会接收您从 React 代码中传入的任何数据。
例如,如果您发出如下操作:
addUser({
name: "John",
email: "john@example.com",
});
那么名为
newUser
的参数将包含:
{
name: "John",
email: "john@example.com"
}
该对象会被作为请求体发送出去。
了解 URL
url: "users",
假设基础 URL 被配置为:
https://jsonplaceholder.typicode.com/
RTK Query 会自动将它们组合成:
https://jsonplaceholder.typicode.com/users
因此您无需手动输入完整地址。
了解方法
method: "POST",
这行代码明确指示 RTK Query 发送 POST 请求。如果省略此行,请求将默认回退为 GET。
了解请求体
body: newUser,
存储在 newUser 中的任何内容都会作为请求载荷被传递,例如:
{
"name": "John",
"email": "john@example.com"
}
服务器会按创建时的原样接收该对象。
步骤 2 — 导出生成的钩子
export const {
useAddUserMutation,
} = api;
就像查询功能会生成自动创建的钩子一样
useGetUsersQuery()
变异操作也会自动生成各自的钩子:
useAddUserMutation()
你无需手动编写这个钩子——RTK Query会根据端点名称为你生成它。
步骤 3 — 创建 React 组件
创建一个新文件:
src/components/AddUser.jsx
并添加以下代码:
import { useState } from "react";
import { useAddUserMutation } from "../services/api";const AddUser = () => { const [name, setName] = useState("");
const [email, setEmail] = useState(""); const [
addUser,
{
isLoading,
isSuccess,
error,
},
] = useAddUserMutation(); const handleSubmit = async (e) => { e.preventDefault(); await addUser({
name,
email,
}); setName("");
setEmail(""); }; return (
<form onSubmit={handleSubmit}> <input
type="text"
placeholder="Enter Name"
value={name}
onChange={(e) => setName(e.target.value)}
/> <input
type="email"
placeholder="Enter Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/> <button type="submit">
Add User
</button> {isLoading && <p>Saving...</p>} {isSuccess && <p>User Added Successfully.</p>} {error && <p>Something went wrong.</p>} </form>
);};export default AddUser;
让我们来分析这里发生的具体过程。
理解 useAddUserMutation()
const [
addUser,
{
isLoading,
isSuccess,
error,
},
] = useAddUserMutation();
与查询钩子不同,变异钩子返回的是数组而非对象。第一个元素为:
addUser
这是用于触发请求的函数,而第二个元素是一个包含该请求相关状态信息的对象。
理解 addUser()
await addUser({
name,
email,
});
调用此函数会发起如下请求:
POST /users
携带的 JSON 数据格式如下:
{
"name": "John",
"email": "john@example.com"
}
在后台,该数据会被用来创建一个全新的用户记录。
理解状态变化
除了触发函数之外,RTK Query 还会提供若干状态标志,用于描述请求的当前处理情况。
isLoading
isLoading
当正在执行数据更新操作时,此标志会变为 true,因此非常适合用于禁用提交按钮或显示加载指示器,直到收到响应为止。
isSuccess
isSuccess
当请求无误完成时,该值将为 true,从而为你提供明确的信号,以便显示确认消息或将用户引导至其他页面。
error
error
如果服务器返回失败响应,相关详细信息会显示在这里,这样你就能呈现可读的错误信息,而不会导致界面异常。
第4步 — 渲染组件
打开主应用程序文件:
src/App.jsx
并将其内容替换为以下内容:
import AddUser from "./components/AddUser";
function App() {
return <AddUser />;
}export default App;
然后启动开发服务器:
npm run dev
填写表单字段并点击Add User——RTK Query会自动为你发送POST请求。
完整的请求流程
以下是从表单提交到状态更新,整个过程中所发生情况的总结:
User Fills Form
│
▼
Clicks Submit
│
▼
addUser()
│
▼
Generated Mutation Hook
│
▼
RTK Query
│
▼
fetchBaseQuery()
│
▼
POST Request
│
▼
Server Response
│
▼
Mutation State Updates
│
▼
React Re-renders
注意此流程中缺失的所有内容:
fetch()axios.post()useEffect()- 手动跟踪加载状态
- 手动跟踪错误状态
RTK Query 在幕后处理了这一切。
builder.query() 与 builder.mutation()
掌握何时使用这些构建器方法非常重要。
builder.query()用于通过GET请求等方式获取数据,它会生成诸如useGetUsersQuery()这样的钩子,在组件渲染时自动执行。相比之下,builder.mutation()则是通过POST、PUT、PATCH或DELETE等方法来修改数据,它生成的钩子如useAddUserMutation(),只有在明确调用触发函数时才会执行,而不会在组件渲染时自动运行。简而言之,查询用于读取数据,而变异操作则用于创建、更新或删除数据。
为每项任务选择合适的工具,能让API逻辑保持一致且易于理解。
最佳实践
使用RTK Query构建POST功能时,请始终牢记以下准则:
- 每当需要修改服务器上的数据时,就使用
builder.mutation()。
isLoading、isSuccess和error状态,以便界面能够及时反馈信息。addUser、createPost或registerUser。try...catch块来处理成功与失败情况,可考虑使用unwrap()函数。核心要点
通过学习本指南,您已经掌握了如何:
- 使用
builder.mutation()创建mutation。 - 在API slice中配置POST端点。
- 将JSON数据发送到后端服务。
- 使用自动生成的
useAddUserMutation()钩子。 - 直接从 React 表单发起 POST 请求。
- 无需手动编写冗余代码即可管理加载、成功和错误状态。
- 区分查询与修改操作。
- 采用最佳实践来构建易于维护的 API 交互。
这种模式在各类生产级应用中随处可见——用户注册流程、身份验证、发布博客文章、下订单以及无数其他数据创建场景。
接下来学什么?
掌握了 POST 请求之后,下一步自然是要学习如何更新和删除现有记录。
即将推出的指南将涵盖:
- 使用 PUT 和 PATCH 请求更新记录。
- 通过 DELETE 请求删除记录。
providesTags 和 invalidatesTags 这两个标签,无需手动重新获取数据即可保持所有内容同步。读完该指南后,您将能够运用适用于生产环境的 RTK Query 模式来构建完整的 CRUD 应用程序。
相关阅读
- TypeScript 6.0 的默认值变更:实用迁移指南 — 了解 TypeScript 6.0 中哪九项编译器默认值发生了变化,如何为 2026 年配置 tsconfig,以及如何为基于 Go 的 TypeScript 7 做好代码库准备。
- 2026年的全栈JavaScript默认技术:TypeScript、RSC及更多 — 阐述了为何在2026年,TypeScript、React Server Components以及更精简的状态管理方式已成为JavaScript团队的标准生产环境技术栈。