首页 / React 19 入门教程 / Actions 表单处理

React 19 入门教程

Actions 表单处理

本教程共 50 篇 · 第 44 篇 · 更新于 2026-07-29 · 约 8 分钟阅读

ReactActions表单处理useActionStateformReact 19

44. Actions 表单处理

本节目标:学会用 React 19 的 Actions 处理表单提交,不再手动写 onSubmit + preventDefault

传统表单的痛点

以前处理表单,你要写一堆样板代码:

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

  async function handleSubmit(e) {
    e.preventDefault();
    setIsSubmitting(true);
    setError(null);
    try {
      await login(username, password);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={username} onChange={e => setUsername(e.target.value)} />
      <input value={password} onChange={e => setPassword(e.target.value)} />
      <button disabled={isSubmitting}>
        {isSubmitting ? '登录中……' : '登录'}
      </button>
      {error && <p>{error}</p>}
    </form>
  );
}

e.preventDefault()isSubmittingtry/catch……每个表单都来一遍。React 19 的 Actions 把这些都简化了。

form action:直接绑定异步函数

React 19 给 <form> 加了 action 属性,可以直接绑定一个异步函数:

function LoginForm() {
  async function loginAction(formData) {
    const username = formData.get('username');
    const password = formData.get('password');
    const result = await login(username, password);
    if (result.error) throw new Error(result.error);
  }

  return (
    <form action={loginAction}>
      <input name="username" />
      <input name="password" type="password" />
      <button type="submit">登录</button>
    </form>
  );
}

不需要 e.preventDefault(),不需要 onSubmit。React 自动阻止默认提交,调用你的 loginAction,把 FormData 传进去。

Note

formData.get('username') 里的 'username' 对应 <input name="username">name 属性。记得给 input 加 name

useActionState:管理表单状态

useActionState 把表单的 pending 状态和结果管理起来:

import { useActionState } from 'react';

function LoginForm() {
  const [state, dispatch, isPending] = useActionState(loginAction, null);

  return (
    <form action={dispatch}>
      <input name="username" />
      <input name="password" type="password" />
      <button type="submit" disabled={isPending}>
        {isPending ? '登录中……' : '登录'}
      </button>
      {state?.error && <p style={{ color: 'red' }}>{state.error}</p>}
    </form>
  );
}

async function loginAction(prevState, formData) {
  const username = formData.get('username');
  const password = formData.get('password');
  try {
    await login(username, password);
    return null;  // 成功,无错误
  } catch (err) {
    return { error: err.message };  // 失败,返回错误信息
  }
}

useActionState 返回三个值:

  • state:action 的返回值(初始是 null
  • dispatch:传给 <form action> 的函数
  • isPending:是否正在提交
Tip

useActionState 的 action 函数接收两个参数:上一次的 state 和 FormData。返回值会成为新的 state。

不用受控组件

React 19 的表单鼓励用非受控组件——input 不加 valueonChange,让浏览器自己管状态:

<form action={dispatch}>
  <input name="username" defaultValue="" />
  <input name="password" type="password" defaultValue="" />
  <button type="submit">登录</button>
</form>

defaultValue 设置初始值,后续由浏览器管理。提交时通过 formData.get('username') 读取。

好处:不用为每个 input 写 useState + onChange,代码少很多。

配合错误边界

action 里抛出的错误会被最近的错误边界捕获:

import { ErrorBoundary } from 'react-error-boundary';

function App() {
  return (
    <ErrorBoundary fallback={<p>提交出错了,请重试</p>}>
      <LoginForm />
    </ErrorBoundary>
  );
}

action 里可以 throw new Error('用户名或密码错误'),错误边界会显示 fallback。

渐进增强

React 19 的表单支持渐进增强——即使 JavaScript 还没加载完,表单也能提交:

async function loginAction(prevState, formData) {
  'use server';  // 服务端函数
  // 在服务端执行登录逻辑
}

加上 'use server' 指令,这个函数会在服务端执行。用户提交表单时,即使 JS 还没下载完,浏览器也会正常提交到服务端。

Note

渐进增强需要框架支持(如 Next.js)。纯客户端的 React 应用用不了 'use server'

多个 submit 按钮

表单里有多个按钮时,想知道用户点了哪个:

async function formAction(formData) {
  const action = formData.get('action');

  if (action === 'save') {
    await saveDraft();
  } else if (action === 'publish') {
    await publish();
  }
}

<form action={formAction}>
  <input name="title" />
  <button type="submit" name="action" value="save">保存草稿</button>
  <button type="submit" name="action" value="publish">发布</button>
</form>

点击哪个按钮,formData.get('action') 就是哪个按钮的 value

与传统方式的对比

传统 onSubmitActions
阻止默认行为手动 e.preventDefault()自动
pending 状态手动 useStateisPending
错误处理手动 try/catch错误边界
非受控组件需要额外处理原生支持
渐进增强不支持支持(需框架)
代码量

Actions 不是要替代所有表单处理。复杂表单(多步骤、动态字段、复杂验证)可能还是用 React Hook Form 这类库更合适。但简单到中等复杂度的表单,Actions 写起来更简洁。