跳到主要内容

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 映射到字段提示。
  • 提交成功后刷新任务列表。