人人都会AI编程

26.2 编译优化与运行时优化

更新时间:2026-07-10

React 19 在表单处理领域带来了两个重要的新 Hook:useActionStateuseFormStatus。它们为表单提交过程中的状态管理提供了原生的声明式支持,让你无需手写大量的 loading、error 状态管理逻辑,也无需引入额外的库即可获得流畅的提交体验。

为什么需要这两个 Hook

传统的表单提交通常需要手动维护 isSubmittingerrorsuccess 等多个状态,代码会迅速变得冗长且容易遗漏边界情况。例如,一个简单的登录表单:

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [error, setError] = useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setIsSubmitting(true);
    setError(null);
    try {
      await loginApi(email, password);
      // 跳转或提示成功
    } catch (err) {
      setError(err.message);
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* ... */}
      <button disabled={isSubmitting}>
        {isSubmitting ? '登录中...' : '登录'}
      </button>
      {error && <p className="error">{error}</p>}
    </form>
  );
}

每个需要异步提交的表单几乎都要重复这套模板。useActionStateuseFormStatus 正是为了简化这种模式而设计的。

useActionState:统一管理 Action 状态

useActionState 是一个专门为 Server Actions 和表单提交动作设计的 Hook,它接收一个异步函数,自动管理该函数执行过程中的状态(数据、错误、pending 状态等),并提供一个触发该函数的 Action 函数。

基本用法:

const [state, formAction, isPending] = useActionState(
  async (previousState, formData) => {
    // 执行异步操作,如调用 Server Action 或 API
    const result = await submitForm(formData);
    return result; // 返回结果会成为新的 state
  },
  initialState // 初始状态
);

返回值:

  • state:当前的状态对象,初始值为 initialState,每次 action 执行成功后由返回值更新。
  • formAction:一个可以直接绑定到 <form action> 的函数,当表单提交时触发。
  • isPending:布尔值,表示异步操作是否正在执行。

实战示例——登录表单:

import { useActionState } from 'react';

async function loginAction(prevState, formData) {
  const email = formData.get('email');
  const password = formData.get('password');
  
  try {
    const user = await loginApi(email, password);
    return { success: true, user };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

function LoginForm() {
  const [state, formAction, isPending] = useActionState(loginAction, {
    success: false,
    error: null,
    user: null,
  });

  if (state.success) {
    return <div>欢迎回来,{state.user.name}!</div>;
  }

  return (
    <form action={formAction}>
      <input type="email" name="email" required />
      <input type="password" name="password" required />
      <button type="submit" disabled={isPending}>
        {isPending ? '登录中...' : '登录'}
      </button>
      {state.error && <p className="error">{state.error}</p>}
    </form>
  );
}

关键点:

  • 不需要 preventDefault,直接使用 <form action={formAction}>,符合 Web 标准。
  • isPending 直接从 Hook 中获取,无需手动切换 loading 状态。
  • 状态(成功/失败)和提示信息都集中管理,组件逻辑更清晰。

useFormStatus:感知表单提交状态

useFormStatus 主要用于子组件中读取当前 <form> 的提交状态,而无需通过 Props 逐层传递。这在设计系统或复杂表单中尤其有用,比如一个提交按钮,它需要根据表单是否正在提交来显示不同的样式和文本,但又不想让每个使用该按钮的父组件都手动传入 isPending

基本用法:

const { pending, data, method, action } = useFormStatus();

返回值:

  • pending:布尔值,当前表单是否正在提交。
  • data:当前正在提交的 FormData 对象(仅在 pending 为 true 时有值)。
  • method:表单的 method 属性('get' 或 'post')。
  • action:绑定在表单上的 action 函数引用。

注意:useFormStatus 必须在某个 <form> 的子组件中才能读取到状态。

实战示例——自定义提交按钮:

import { useFormStatus } from 'react';

function SubmitButton() {
  const { pending } = useFormStatus();
  
  return (
    <button type="submit" disabled={pending}>
      {pending ? '提交中...' : '提交'}
    </button>
  );
}

// 在表单中使用
function MyForm() {
  const [state, formAction] = useActionState(handleSubmit, {});
  
  return (
    <form action={formAction}>
      <input name="title" />
      <SubmitButton />
    </form>
  );
}

这样 SubmitButton 可以复用在任何表单中,自动感知提交状态,无需父组件额外传参。

两者结合使用的最佳实践

useActionState 管理整体表单提交的状态和逻辑,useFormStatus 则让任何深层的表单组件都能感知到提交状态,实现高度解耦。

更完整的示例——带乐观更新的评论表单:

import { useActionState, useFormStatus } from 'react';
import { addComment } from './actions';

function CommentForm({ postId }) {
  const [state, formAction, isPending] = useActionState(
    async (prevState, formData) => {
      const content = formData.get('content');
      if (!content.trim()) {
        return { error: '评论内容不能为空' };
      }
      await addComment(postId, content);
      return { success: true };
    },
    { success: false, error: null }
  );

  return (
    <form action={formAction}>
      <textarea name="content" placeholder="写下你的评论..." />
      <SubmitCommentButton />
      {state.error && <p className="error">{state.error}</p>}
      {state.success && <p className="success">评论发布成功!</p>}
    </form>
  );
}

function SubmitCommentButton() {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? '发布中...' : '发布评论'}
    </button>
  );
}

注意事项与局限性

  1. 环境要求useActionStateuseFormStatus 是 React 19 新增的 Hook,需要配合 React DOM 19 使用。它们天然支持 Server Actions,但也完全可以在纯客户端表单中使用普通的异步函数。
  1. 与普通受控表单的兼容:当使用 action 属性时,表单默认为非受控模式(通过 FormData 获取数据)。如果你习惯使用受控组件(value + onChange),可以继续使用 onSubmit 模式,但此时无法充分利用 useFormStatus 的自动感知能力(除非你手动包裹 <form>)。React 19 推荐优先使用 action 模式来简化表单逻辑。
  1. 错误处理与重试useActionState 的核心思想是“每次提交返回一个新状态”,因此错误信息也应通过返回值传递,而不是抛出异常。你可以在 action 函数内部统一处理异常,将其转化为状态对象返回。
  1. 乐观更新:虽然 useActionState 可以结合 useOptimistic 实现乐观更新(见下一节),但它本身不内置乐观更新逻辑。你需要根据 stateisPending 手动渲染乐观 UI。

总结

useActionStateuseFormStatus 让表单处理进入了一个新的阶段:代码更接近 Web 标准、状态管理更自动、组件复用更自然。它们与 Server Actions 深度集成,为 React 19 的全栈能力奠定了表单这一关键领域的基础。在开发新项目时,这两个 Hook 应当成为你处理表单的首选方案。