在 React 中处理表单输入是常见且重要的任务。由于 React 的“单向数据流”和“状态驱动视图”特性,表单处理与原生 JavaScript 有所不同。主要方法分为两大类:受控组件 (Controlled Components) 和 非受控组件 (Uncontrolled Components)。
一、受控组件 (Controlled Components) – 推荐方式
这是 React 中处理表单的标准和推荐做法。表单元素的值由 React 组件的 state 控制,用户输入通过事件处理器实时更新 state,state 的变化又驱动视图更新。
核心思想
- 数据源单一:表单的值(
value)由 React 的state决定。 - 双向绑定:通过
onChange事件监听用户输入,并立即更新state。
基本示例
import React, { useState } from 'react';
function NameForm() {
const [name, setName] = useState('');
const handleChange = (e) => {
setName(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
alert('提交的名字: ' + name);
};
return (
<form onSubmit={handleSubmit}>
<label>
名字:
<input
type="text"
value={name} // 值由 state 控制
onChange={handleChange} // 输入时更新 state
/>
</label>
<button type="submit">提交</button>
</form>
);
}
处理多种输入类型
可以使用一个通用的 handleChange 函数处理多个输入字段。
function UserForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
isSubscribed: false
});
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
// 对于复选框,使用 checked 属性
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
};
return (
<form onSubmit={handleSubmit}>
<input
name="name"
value={formData.name}
onChange={handleChange}
placeholder="姓名"
/>
<input
name="email"
value={formData.email}
onChange={handleChange}
placeholder="邮箱"
/>
<label>
<input
name="isSubscribed"
type="checkbox"
checked={formData.isSubscribed}
onChange={handleChange}
/>
订阅邮件
</label>
<button type="submit">提交</button>
</form>
);
}
优点
- ✅ 数据一致性:React state 是唯一数据源,易于管理和调试。
- ✅ 实时验证:可以在
onChange中立即进行输入验证。 - ✅ 灵活控制:可以轻松格式化输入、限制字符等。
- ✅ 符合 React 思想:声明式 UI,状态驱动。
缺点
- ❌ 样板代码较多:每个输入都需要
state和onChange处理器。 - ❌ 性能:对于大量输入,频繁的
setState可能有轻微性能开销(通常可忽略)。
二、非受控组件 (Uncontrolled Components)
非受控组件将表单数据的管理交给 DOM 本身,而不是 React 的 state。使用 ref 来直接访问 DOM 元素,获取其值。
核心思想
- 数据源在 DOM:表单元素自己管理自己的值。
- 通过 ref 获取值:在需要时(如提交时)通过
ref读取 DOM 的当前值。
基本示例
import React, { useRef } from 'react';
function NameForm() {
const inputRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
// 通过 ref 访问 DOM 元素的值
alert('名字: ' + inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<label>
名字:
<input type="text" ref={inputRef} />
</label>
<button type="submit">提交</button>
</form>
);
}
设置默认值
使用 defaultValue 或 defaultChecked 属性。
<input
defaultValue="初始值"
ref={inputRef}
/>
优点
- ✅ 简单直接:对于简单表单或不需要实时控制的场景,代码更少。
- ✅ 集成非 React 代码:方便与需要直接操作 DOM 的第三方库(如 jQuery 插件)集成。
- ✅ 性能:避免了频繁的
setState调用。
缺点
- ❌ 数据分散:表单数据不在 React state 中,难以进行集中管理和调试。
- ❌ 无法实时验证:不能在输入过程中实时响应和验证。
- ❌ 灵活性差:难以动态控制输入值(如格式化、限制)。
- ❌ 不符合 React 理念:更像是原生 JavaScript 的做法。
三、使用第三方库
对于复杂的表单(如嵌套结构、复杂验证、动态字段),手动管理状态会变得繁琐。可以使用成熟的表单库:
- React Hook Form:
- 理念:推崇非受控组件,性能极高。
- 特点:体积小、性能好、支持 Yup 等验证库、类型安全。
- 适合:大多数场景,尤其是性能敏感的大型表单。
- Formik:
- 理念:基于受控组件。
- 特点:功能全面,API 友好,社区成熟。
- 适合:需要复杂逻辑和验证的表单。
React Hook Form 示例:
import { useForm } from 'react-hook-form';
function MyForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("name", { required: true })} />
{errors.name && <span>名字是必填的</span>}
<input type="submit" />
</form>
);
}
四、总结与选择
| 方法 | 适用场景 | 推荐度 |
|---|---|---|
| 受控组件 | 需要实时验证、动态控制、数据集中管理的表单。现代 React 的标准做法。 | ✅✅✅ 强烈推荐 |
| 非受控组件 | 简单表单、需要与非 React 库集成、性能要求极高且无需实时控制的场景。 | ⚠️ 特定场景使用 |
| 第三方库 (如 React Hook Form) | 复杂表单、需要强大验证、减少样板代码的项目。 | ✅✅ 大型/复杂项目推荐 |
结论: 对于大多数 React 应用,受控组件是首选方法。它与 React 的设计理念完美契合,提供了更好的控制力和可维护性。当表单变得复杂时,应考虑使用 React Hook Form 这样的库来简化开发。非受控组件仅在特定需求下作为补充。
THE END


