DOM 表单组件
如何保持受控输入与状态同步?
- 文本控制: input、textarea 和 select 的 value 来自 state,并在 onChange 中同步更新,不在同一生命周期内从 undefined 切换为受控字符串;
- 勾选控制: checkbox/radio 使用 checked 与 event.target.checked,不能用文本 value 代替布尔勾选状态;
- 只读意图: 提供 value 或 checked 却不允许编辑时明确设置 readOnly,避免看似能输入却立刻恢复;
const [name, setName] = useState("");
const [agreed, setAgreed] = useState(false);
const fields = (
<>
<input value={name} onChange={(event) => setName(event.target.value)} />
<input type="checkbox" checked={agreed} onChange={(event) => setAgreed(event.target.checked)} />
</>
);
如何让浏览器保存非受控输入?
- 初始值: 使用 defaultValue 或 defaultChecked,只设置初始内容,之后由 DOM 保存用户输入;
- 读取提交: 为控件设置 name,提交时从 FormData 读取;多选字段用 getAll,直接 Object.fromEntries 会丢失同名多值;
- 类型差异: textarea 用 value/defaultValue 而非 children,select 由父元素的 value 控制而非 option.selected,多选 select 使用数组;
function TagsForm({ saveTags }) {
function submit(formData) {
return saveTags(formData.getAll("tags"));
}
return (
<form action={submit}>
<select name="tags" multiple defaultValue={["react"]}>
<option value="react">React</option>
<option value="typescript">TypeScript</option>
</select>
<button>保存标签</button>
</form>
);
}
函数 action 如何处理表单提交?
- 提交载荷:
<form action={fn}>调用函数时传入 FormData,函数 action 使用 POST 语义,成功后重置非受控字段; - 提交按钮: button 的 formAction 可以覆盖 form 的 action,按钮 name/value 可标识用户选择的操作;非提交按钮显式 type="button";
- 状态组合: pending 展示见表单提交状态,结果与错误消息见 Action 状态,避免把三个契约混成一个 Hook;
async function submit(formData) {
await save(String(formData.get("message")));
}
const form = (
<form action={submit}>
<textarea name="message" defaultValue="" />
<button type="submit">发送</button>
</form>
);
如何保证表单可访问且提交数据完整?
- 标签关联: label 包裹控件或通过 htmlFor/id 对应,唯一关联 ID 的生成见标识与调试 Hooks;
- 浏览器校验: required、min、max 等约束改善交互,但服务端仍要独立验证输入和权限;
- 进度指示: progress 的 value/max 表达完成度,不确定进度时省略 value,不把 0 当作“不确定”;