解读 React Router Server Action。
这段代码与之前的 clientAction
非常相似,但有一个核心区别:数据处理逻辑在服务器端执行。这为处理需要安全验证或直接访问数据库等敏感操作提供了保障。
我们同样分两部分来看:action
函数和 Project
组件。
Server action
函数
这个 action
函数被设计为只在服务器上运行。当表单被提交时,React Router 会将请求发送到你的服务器,并由服务器执行这个函数。
jsx
// 这个函数只在服务器上运行
// 它不会被打包进发送给浏览器的 JavaScript 文件中
export async function action({
request,
}: Route.ActionArgs) {
// 1. 在服务器上获取表单数据
let formData = await request.formData();
let title = formData.get("title");
// 2. 在服务器上直接与数据库交互
let project = await fakeDb.updateProject({ title });
// 3. 从服务器返回更新后的数据
return project;
}
代码分步解析:
await request.formData()
: 当用户提交表单后,React Router 会向当前页面的 URL 发送一个POST
请求。服务器接收到这个请求,这里的代码在服务器环境中解析出表单数据。await fakeDb.updateProject({ title })
: 这是最关键的一步。因为这段代码在服务器上运行,所以它可以安全地导入并调用服务器端的模块,比如直接操作数据库的fakeDb
。这些敏感的数据库操作逻辑永远不会暴露给用户的浏览器。return project
: 函数执行完毕后,会将project
数据序列化(通常是转为 JSON),并通过网络响应发送回用户的浏览器。
核心优势:
- 安全性:数据库凭证、私有 API 密钥和核心业务逻辑都保留在服务器上,永远不会发送到客户端,极大地提高了应用的安全性。
- 代码分离 :
action
函数及其依赖(如fakeDb
)会自动从客户端的 JavaScript 包中移除,减小了浏览器需要下载的文件体积,提升了加载性能。
Project
组件
组件部分的代码与 clientAction
的例子完全一样,但这正是 React Router 设计的巧妙之处。作为开发者,你不需要改变组件的写法,框架会自动处理数据是通过客户端还是服务器端提交的。
jsx
export default function Project({
actionData,
}: Route.ComponentProps) {
return (
<div>
<h1>Project</h1>
{/* A. <Form> 的行为发生了变化 */}
<Form method="post">
<input type="text" name="title" />
<button type="submit">Submit</button>
</Form>
{/* B. 同样接收 action 的返回值并更新 UI */}
{actionData ? (
<p>{actionData.title} updated</p>
) : null}
</div>
);
}
代码分步解析:
A. <Form method="post">
: 在这个场景下,React Router 的 <Form>
组件的行为是:
- 拦截用户的提交事件,阻止页面刷新。
- 创建一个标准的
POST
网络请求,将表单数据发送到当前页面的 URL。 - 等待服务器的响应。
B. actionData
Prop : 这个 prop 的数据来源变了。它现在接收的是从服务器 响应中返回的数据。当表单提交、服务器上的 action
函数成功执行并返回数据后,React Router 会捕获这个响应,并将数据通过 actionData
prop 传递给组件,从而触发 UI 更新。
总结整个流程
这个 Server Action 的流程涉及一次完整的客户端-服务器通信:
- 用户操作:用户在浏览器中输入标题并点击 "Submit"。
- 网络请求 :React Router 的
<Form>
组件将表单数据打包,向服务器发起一个POST
请求。 - 服务器处理 :服务器接收到请求,调用与该路由匹配的
action
函数。 - 数据变更 :
action
函数在服务器上安全地执行数据库更新操作。 - 网络响应 :
action
函数将更新后的project
对象作为响应返回给浏览器。 - UI 更新 :在浏览器端,React Router 接收到这个响应数据,并将其注入到
Project
组件的actionData
prop 中。 - 显示反馈 :组件因
actionData
变化而重新渲染,无缝地向用户展示更新成功的消息。
对比 clientAction
与 Server action
clientAction
:在浏览器中运行,适合执行纯客户端逻辑,如调用公开的第三方 API 或在发送前进行数据校验。反馈更快,因为它不涉及网络往返。- Server
action
:在服务器上运行,是处理需要保护的业务逻辑或直接操作后端资源的理想选择。它更安全、更强大。
React Router 允许你在同一个路由中同时定义 clientAction
和 action
。在这种情况下,clientAction
会优先执行,这为你实现更复杂的交互模式(如乐观更新)提供了可能。