Actions 表单处理
本教程共 50 篇 · 第 44 篇 · 更新于 2026-07-29 · 约 8 分钟阅读
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()、isSubmitting、try/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 不加 value 和 onChange,让浏览器自己管状态:
<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。
与传统方式的对比
| 传统 onSubmit | Actions | |
|---|---|---|
| 阻止默认行为 | 手动 e.preventDefault() | 自动 |
| pending 状态 | 手动 useState | isPending |
| 错误处理 | 手动 try/catch | 错误边界 |
| 非受控组件 | 需要额外处理 | 原生支持 |
| 渐进增强 | 不支持 | 支持(需框架) |
| 代码量 | 多 | 少 |
Actions 不是要替代所有表单处理。复杂表单(多步骤、动态字段、复杂验证)可能还是用 React Hook Form 这类库更合适。但简单到中等复杂度的表单,Actions 写起来更简洁。