4.说一下React 19 对表单处理的改进:useActionState 和 useFormStatus? 来源:https://w0hog67yl81.feishu.cn/wiki/LlTswRTfWiH6z6kTDvYc10rcntf 采集状态:已完成正文采集;正文滚动至底部,按块及代码行号采集 React 19 表单处理的变革:useActionState 与 useFormStatus 详解​ ​ 在 Web 开发中,表单处理是构建用户交互的核心环节。长久以来,React 开发者们习惯于使用 useState 来管理表单的各种状态,例如输入数据、提交中的 pending 状态、以及错误信息。这种方式虽然直观,但在复杂场景下往往会导致组件内部状态逻辑变得冗长和分散。​ ​ React 19 的到来,为我们带来了两个强大的新 Hooks:useActionState 和 useFormStatus。它们与 React Actions 协同工作,旨在简化表单状态管理,提供更优雅、更集中的逻辑处理方式,并优化用户体验。​ ​ 回顾:传统表单的状态管理​ ​ 在深入了解新特性之前,我们先回顾一下在 React 19 之前是如何处理表单异步提交的。通常,我们需要手动维护至少三个状态:​ ​ 1. Pending 状态:用于在数据提交过程中禁用按钮、显示加载指示器。​ 2. Error 状态:用于捕获并向用户展示提交过程中发生的错误。​ 3. Data 状态:用于存储和更新表单数据。 让我们看一个典型的例子,一个更新用户名的表单:​ ​ // 在 React 19 之前的做法 function UpdateNameComponent() { const [name, setName] = useState(""); const [error, setError] = useState(null); const [isPending, setIsPending] = useState(false); const handleSubmit = async () => { setIsPending(true); const submissionError = await updateName(name); // 假设 updateName 是一个异步请求 setIsPending(false); if (submissionError) { setError(submissionError); return; } redirect("/success-path"); // 提交成功后重定向 }; return (
setName(e.target.value)} /> {error &&

{error}

}
); } ​ 这段代码清晰地暴露了问题:isPending 和 error 的状态管理与核心的提交逻辑 handleSubmit 纠缠在一起,增加了代码的复杂性。随着表单逻辑的增加,这些状态管理代码会变得越来越难以维护。​ ​ useActionState:优雅地管理 Action 状态​ ​ React 19 引入了 Actions 的概念,允许我们将一个函数直接传递给
的 action 属性。useActionState Hook 则是专门为管理这些 Action 的状态而设计的。它接收一个 Action 函数和初始状态,返回一个包含当前状态、可供表单调用的新 Action,以及一个 pending 状态的数组。​ ​ 它的签名如下:​ ​ const [state, formAction, isPending] = useActionState(actionFn, initialState); ​ • state:Action 执行后返回的状态。初始值为 initialState。​ • formAction:一个新的 Action,需要将其传递给 的 action 属性。React 会在表单提交时调用它。​ • isPending:一个布尔值,表示 Action 是否正在执行中。 现在,我们用 useActionState 来重构上面的例子:​ ​ // 使用 useActionState import { useActionState } from 'react'; function ChangeName({ name, setName }) { const [error, submitAction, isPending] = useActionState( async (previousState, formData) => { const newName = formData.get("name"); const error = await updateName(newName); // 同样的异步请求 if (error) { // 直接返回错误,它将成为新的 state return error; } redirect("/path"); return null; }, null, // 初始 error 状态为 null ); return ( {error &&

{error}

}
); } ​ 通过对比可以发现,useActionState 极大地简化了代码:​ ​ • 状态集中管理:将 pending 和 error 状态的处理逻辑收敛到了 useActionState 内部。​ • 代码更简洁:我们不再需要手动调用 setIsPending 和 setError。Action 函数的返回值就是新的状态。​ • 语义更明确:代码的意图从“手动处理点击事件”转变为“声明一个表单 Action”,更加符合 React 的声明式思想。 useFormStatus:感知父表单的提交状态​ ​ 在实际应用中,提交按钮或状态提示通常是独立的子组件。useActionState 的 isPending 状态在当前组件作用域内可用,但如果想在子组件中响应表单的提交状态,就需要通过 props 逐层传递,这非常繁琐。​ ​ useFormStatus Hook 优雅地解决了这个问题。它允许一个组件访问其所在的父级
的状态信息,而无需任何 props 传递。​ ​ 重要前提:使用 useFormStatus 的组件必须被渲染在 标签内部。 ​ useFormStatus 返回一个包含表单状态的对象,其中最常用的就是 pending 属性。​ ​ import { useFormStatus } from 'react-dom'; function SubmitButton() { const { pending } = useFormStatus(); return ( ); } // 在表单中使用 function SomeForm() { return ( ); } ​ 在这个例子中,SubmitButton 组件不需要从 SomeForm 接收任何 props。它通过 useFormStatus 直接“感知”到外部
是否处于提交中,并相应地更新自己的 UI。这使得创建可复用的、与表单状态解耦的设计系统组件变得异常简单。​ ​ 综合实践:构建一个购物车表单​ ​ 现在,我们将 useActionState 和 useFormStatus 结合起来,构建一个功能完整的“添加到购物车”的表单。这个例子将展示如何处理成功和失败两种情况,并提供即时反馈。​ ​ actions.js (Server Action)​ ​ 我们首先定义一个在服务器上运行的 Action。​ ​ 'use server'; export async function addToCart(prevState, queryData) { const itemID = queryData.get('itemID'); // 模拟 API 请求延迟 await new Promise(resolve => setTimeout(resolve, 1000)); if (itemID === "1") { // 模拟成功 return { success: true, cartSize: 12, // 假设返回了新的购物车数量 }; } else { // 模拟失败 return { success: false, message: "该商品已售罄。", }; } } ​ AddToCartForm.js (Client Component)​ ​ import { useActionState } from "react"; import { useFormStatus } from "react-dom"; import { addToCart } from "./actions.js"; function SubmitButton() { const { pending } = useFormStatus(); return ( ); } function AddToCartForm({ itemID, itemTitle }) { // useActionState 管理 Action 的返回状态 const [formState, formAction] = useActionState(addToCart, {}); return (

{itemTitle}

{/* 根据 Action 返回的状态显示不同的提示信息 */} {formState?.success && (
添加成功!购物车现在有 {formState.cartSize} 件商品。
)} {formState?.success === false && (
添加失败: {formState.message}
)} ); } export default function App() { return ( <> ); } ​ 在这个综合示例中:​ ​ 1. AddToCartForm 使用 useActionState 来调用 addToCart Action 并接收其返回的状态 formState。​ 2. formState 对象包含了 success、cartSize 或 message 等信息,组件可以直接用它来渲染不同的 UI 反馈。​ 3. SubmitButton 作为子组件,使用 useFormStatus 来独立管理自己的 disabled 和文本状态,完全不知道外部表单的具体逻辑。​ 4. 整个流程实现了状态管理与 UI 组件的完美分离,代码结构清晰,可维护性高。 结论​ ​ useActionState 和 useFormStatus 是 React 19 在表单处理方面迈出的重要一步。它们不仅显著减少了管理异步表单状态所需的模板代码,还通过关注点分离(Separation of Concerns)的原则,使得构建可复用、可预测的表单组件变得更加容易。​ ​ 当我们拥抱这些新特性时,我们实际上是在拥抱一种更声明式、更强大的范式来处理用户交互。下一次你在 React 中构建表单时,不妨尝试一下这两个新的 Hooks,体验它们为开发流程带来的顺滑与高效。​ ​