6.3.2 React 表单与状态
React 表单的重点不是把输入框变成受控组件,而是把用户输入、提交中、校验失败、服务端错误和成功反馈建模清楚。
学习目标
- 能用 TypeScript 定义表单状态。
- 能处理受控输入和提交事件。
- 能展示客户端校验和服务端错误。
- 能避免重复提交和状态竞争。
应用场景
- 创建任务、编辑用户资料、登录注册。
- 前后端分离页面需要调用 ASP.NET Core API。
- 后端返回字段级错误,需要在表单中展示。
表单状态模型
type CreateTodoForm = {
title: string;
description: string;
};
type SubmitState =
| {status: 'idle'}
| {status: 'submitting'}
| {status: 'success'}
| {status: 'error'; message: string; fieldErrors?: Record<string, string[]>};
示例组件
import {FormEvent, useState} from 'react';
import {createTodo} from './todoApi';
export function CreateTodoForm() {
const [form, setForm] = useState<CreateTodoForm>({title: '', description: ''});
const [submitState, setSubmitState] = useState<SubmitState>({status: 'idle'});
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
if (!form.title.trim()) {
setSubmitState({
status: 'error',
message: '请修正表单错误',
fieldErrors: {title: ['标题不能为空']},
});
return;
}
setSubmitState({status: 'submitting'});
try {
await createTodo({
title: form.title.trim(),
description: form.description.trim() || undefined,
});
setForm({title: '', description: ''});
setSubmitState({status: 'success'});
} catch {
setSubmitState({status: 'error', message: '创建任务失败'});
}
}
const titleErrors = submitState.status === 'error'
? submitState.fieldErrors?.title ?? []
: [];
return (
<form onSubmit={handleSubmit}>
<label>
标题
<input
value={form.title}
onChange={(event) => setForm({...form, title: event.target.value})}
/>
</label>
{titleErrors.map((error) => <p role="alert" key={error}>{error}</p>)}
<label>
备注
<textarea
value={form.description}
onChange={(event) => setForm({...form, description: event.target.value})}
/>
</label>
<button type="submit" disabled={submitState.status === 'submitting'}>
{submitState.status === 'submitting' ? '提交中...' : '创建任务'}
</button>
</form>
);
}
重点难点
- 用状态机思维描述提交状态,比多个布尔值更清晰。
- 前端字段名要和后端错误契约保持一致。
- 提交时要禁用按钮,避免重复请求。
- 对复杂表单可引入成熟表单库,但基础状态模型仍要理解。
常见误区
| 误区 | 推荐做法 |
|---|---|
只用一个 isLoading 表示所有状态 | 区分 idle、submitting、success、error |
| API 错误只弹窗 | 字段错误展示在对应输入旁 |
| 提交后不清理状态 | 成功、失败和再次编辑都要有明确状态变化 |
练习
- 给描述字段增加 500 字限制。
- 把后端返回的
errors映射到字段提示。 - 提交成功后刷新任务列表。