react-router-action
5 min read
Table of Contents
核心概念
-
Action:
- 路由级别的异步函数,用于处理数据提交。
- 在组件提交表单(Form)后执行。
- 自动重新验证 (Revalidation): Action 执行成功后,React Router 会自动重新运行页面上所有的 Loader,确保 UI 显示最新的数据。
-
命名规则 (重要):
- SPA 模式 (
ssr: false): 必须导出为clientAction。 - SSR 模式 (
ssr: true): 服务端处理导出action,客户端处理导出clientAction。
- SPA 模式 (
基本用法
1. 定义 Action
import type { ActionFunctionArgs } from "react-router";import { redirect } from "react-router";
// SPA 模式下使用 clientActionexport async function clientAction({ request, params }: ActionFunctionArgs) { // 1. 获取表单数据 const formData = await request.formData(); const title = formData.get("title");
// 2. 数据校验 if (typeof title !== "string" || title.length === 0) { return { error: "标题不能为空" }; }
// 3. 执行副作用 (API 调用) await createPost({ title });
// 4. 返回结果或重定向 return redirect("/posts");}2. 触发 Action
方式 A: <Form> 组件 (全页跳转)
适用于普通的页面级表单提交。
import { Form, useActionData } from "react-router";
export default function NewPost() { const actionData = useActionData<typeof clientAction>();
return ( <Form method="post"> <input name="title" /> {actionData?.error && <p>{actionData.error}</p>} <button type="submit">提交</button> </Form> );}方式 B: useFetcher (局部更新)
适用于不进行页面跳转的操作(如点赞、加入购物车、弹窗表单)。
import { useFetcher } from "react-router";
export default function LikeButton() { const fetcher = useFetcher<typeof clientAction>(); const isSubmitting = fetcher.state === "submitting";
return ( <fetcher.Form method="post" action="/like"> <button type="submit" disabled={isSubmitting}> {isSubmitting ? "点赞中..." : "点赞"} </button> </fetcher.Form> );}方式 C: 编程式提交 (Antd 等第三方库集成)
当使用 Antd Form 的 onFinish 时,手动调用 fetcher.submit。
const fetcher = useFetcher();
const onFinish = (values) => { // 提交数据到 Action fetcher.submit(values, { method: "post" });};
// 获取 Action 返回值const result = fetcher.data;常见坑点 (Pitfalls) 🚨
1. 手动调用 Action 函数
- 错误:
const res = await clientAction(...) - 后果: 失去了 React Router 的核心优势(加载状态管理、自动重新验证 Loader)。
- 正确: 必须通过
<Form>或fetcher.submit触发。
2. SPA 模式下的命名
- 错误: 在
ssr: false项目中导出export async function action(...)。 - 后果: 报错
invalid route export或 Action 不生效。 - 正确: 必须命名为
clientAction。
3. FormData 与 JSON 的混淆
- 现象: 在
clientAction中await request.json()报错。 - 原因: 默认
<Form>和fetcher.submit提交的是application/x-www-form-urlencoded或multipart/form-data。 - 解决:
- 标准做法是使用
request.formData()。 - 如果必须发 JSON,需在 submit 时指定
encType:
- 标准做法是使用
fetcher.submit(data, { method: "post", encType: "application/json" });然后在 Action 中使用 request.json()。
4. 第三方 UI 库 (Antd) 的嵌套对象
- 现象: Antd Form 返回
{ user: { name: "xxx" } },直接传给fetcher.submit后,在 Action 中formData.get('user.name')取不到值。 - 原因:
fetcher.submit默认会将对象扁平化为 FormData。 - 解决:
- 方案一:手动扁平化键名
{ "user.name": values.user.name }。 - 方案二:使用
encType: "application/json"提交 JSON 格式。
- 方案一:手动扁平化键名
5. Action 不会重新渲染当前组件的 Loader 数据?
- 机制: 默认情况下,Action 成功后会重新验证所有活跃路由的 Loader。
- 例外: 如果你在 Action 中返回了非 2xx/3xx 状态码(比如 400校验错误),Loader 不会重新运行。
- 坑点: 如果你想在校验错误时也刷新某些数据,需要注意这一点。
6. fetcher.data vs useActionData
useActionData: 获取当前页面 URL 对应的 Action 返回值(通常用于<Form>提交)。fetcher.data: 获取特定 fetcher 实例对应的 Action 返回值(用于局部交互)。- 混用后果: 在
useFetcher提交后去读useActionData是拿不到结果的。
评论 (0)
请先登录后再发表评论