1. 核心定义
-
受控组件(Controlled Component)
表单数据完全由React的state管理,通过onChange事件同步更新状态,输入值由value属性绑定到React状态。
示例:const [inputValue, setInputValue] = useState(""); <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} /> -
非受控组件(Uncontrolled Component)
表单数据由DOM自身管理,React通过ref直接访问DOM节点的值(通常在提交时读取)。
示例:const inputRef = useRef(null); <input defaultValue="初始值" ref={inputRef} /> // 提交时获取值:inputRef.current.value
2. 核心差异对比
| 特性 | 受控组件 | 非受控组件 |
|---|---|---|
| 数据流 | 单向数据流:state → 表单 → onChange → state。 | 双向数据流:用户输入直接修改DOM,React通过ref被动获取值。 |
| 实时控制能力 | ✅ 支持实时验证、格式化输入(如手机号自动分隔)。 | ❌ 只能在提交时获取值,无法实时干预输入。 |
| 动态UI更新 | ✅ 输入变化可触发UI更新(如动态提示、联动表单)。 | ❌ 无法直接触发UI更新,需手动操作DOM或触发其他事件。 |
| 代码复杂度 | 较高(需为每个表单元素绑定value和onChange)。 | 较低(无需事件监听,直接通过ref获取值)。 |
| 性能影响 | 可能因频繁状态更新和渲染影响性能(大型表单需优化)。 | 性能更优(无频繁状态更新)。 |
| 适用场景 | 复杂表单交互(如即时搜索、动态表单验证)。 | 简单表单、文件上传、集成非React代码(如第三方库)。 |
| 表单重置 | 直接通过更新state重置。 | 需手动操作DOM(如inputRef.current.value = "")。 |
3. 代码实现对比
受控组件示例(实时验证邮箱格式)
function EmailForm() {
const [email, setEmail] = useState("");
const [isValid, setIsValid] = useState(false);
const handleChange = (e) => {
const value = e.target.value;
setEmail(value);
setIsValid(/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value));
};
return (
<div>
<input type="email" value={email} onChange={handleChange} />
{!isValid && <p style={{ color: "red" }}>邮箱格式不正确</p>}
</div>
);
}
非受控组件示例(文件上传)
function FileUpload() {
const fileRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
const file = fileRef.current.files[0];
console.log("上传文件:", file.name);
};
return (
<form onSubmit={handleSubmit}>
<input type="file" ref={fileRef} />
<button type="submit">上传</button>
</form>
);
}
4. 优缺点总结
| 类型 | 优点 | 缺点 |
|---|---|---|
| 受控组件 | - 完全控制数据流。 - 支持实时交互和验证。 - 符合React设计理念。 | - 代码冗余。 - 性能开销可能较大。 |
| 非受控组件 | - 代码简洁。 - 性能更高。 - 适合文件上传等特定场景。 | - 无法实时控制输入。 - 依赖DOM操作,违背React数据流原则。 |
5. 使用场景建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 动态表单验证 | 受控组件 | 实时验证输入有效性(如密码强度、邮箱格式)。 |
| 复杂联动表单 | 受控组件 | 根据输入值动态更新其他表单字段或UI元素。 |
| 文件上传 | 非受控组件 | <input type="file">必须通过ref获取文件对象(浏览器限制)。 |
| 简单的一次性提交表单 | 非受控组件 | 减少代码量,无需复杂状态管理。 |
| 集成第三方UI库(如jQuery) | 非受控组件 | 直接操作DOM更便捷,避免与React状态冲突。 |
6. 高级技巧与注意事项
-
受控组件的性能优化
使用useMemo缓存计算结果,或拆分表单为独立子组件(通过React.memo避免父组件渲染导致子组件重绘)。 -
非受控组件的默认值
通过defaultValue或defaultChecked设置初始值(仅在首次渲染生效)。 -
混合使用场景
在复杂表单中,关键字段使用受控组件(如用户名、密码),非关键字段使用非受控组件(如备注信息)。 -
表单重置
- 受控组件:直接更新
state即可。 - 非受控组件:需要手动操作DOM(如
inputRef.current.value = "")。
- 受控组件:直接更新
7. 总结
- 优先选择受控组件:符合React数据流理念,适合大多数需要精细控制的场景。
- 合理使用非受控组件:在文件上传、简单表单或集成第三方库时更高效。
- 性能与代码简洁的权衡:根据项目需求选择,复杂交互优先受控,性能敏感场景考虑非受控。

1155

被折叠的 条评论
为什么被折叠?



