- 受控组件:数据由 React 组件的 State 掌控。
- 非受控组件:数据由 DOM 节点本身 掌控。
下面我把这两者掰开了揉碎了给你讲透。
一、 受控组件 (Controlled Components)
这是 React 官方推荐的“正统”写法。
1. 原理
React 组件的状态(state)是表单数据的唯一来源。
- 显示: 输入框的值绑定到
state.value。 - 修改: 用户输入触发
onChange事件 -> 调用setState-> React 重新渲染 -> 输入框显示新值。
形成了一个闭环:Render -> User Input -> State Update -> Re-render。
2. 代码示例
function ControlledInput() {
const [text, setText] = useState('');
const handleChange = (e) => {
// 你可以在这里做任何事:大写转换、校验、过滤
const upperText = e.target.value.toUpperCase();
setText(upperText);
};
return (
// value 绑定 state,onChange 负责更新 state
<input value={text} onChange={handleChange} />
);
}
3. 特点
- 数据完全同步: React 永远知道输入框里现在是什么。
- 控制力强: 你可以轻松截断用户输入(比如只能输数字)、实时校验(边打字边报错)。
二、 非受控组件 (Uncontrolled Components)
这更像传统的 HTML/JS 写法,React 只是作为一个“旁观者”。
1. 原理
React 不管输入框里显示什么,数据储存在 DOM 元素内部。
- 显示: 使用
defaultValue设置初始值(之后就不管了)。 - 获取: 当你需要数据时(通常是点击提交按钮时),通过 Ref 去 DOM 里把数据“拉”出来。
2. 代码示例
function UncontrolledInput() {
const inputRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
// 只有在这一刻,React 才去读取 DOM 的值
alert('提交的值: ' + inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
{/* 注意:这里用的是 defaultValue,没有 value 和 onChange */}
<input type="text" defaultValue="初始值" ref={inputRef} />
<button type="submit">提交</button>
</form>
);
}
3. 特点
- 更接近原生: 操作方式和 jQuery 或原生 JS 操作 DOM 一样。
- 性能较好: 输入字符不会触发 React 组件的重新渲染(Re-render)。
三、 核心区别 (对比总结)
| 特性 | 受控组件 (Controlled) | 非受控组件 (Uncontrolled) |
|---|---|---|
| 数据来源 | React State | DOM 自身 |
| 数据获取 | 从 State 中直接读取 | 通过 Ref 从 DOM 读取 |
| 实时性 | 实时更新,每次按键都触发重渲染 | 只有在需要时(如提交)才获取 |
| 值控制 | value 属性 | defaultValue 属性 |
| 性能 | 较差(每次输入都 Render) | 较好(输入不触发 Render) |
| 逻辑复杂度 | 需编写 onChange 处理逻辑 | 代码通常更少,但逻辑分散 |
四、 应用场景 (高级开发怎么选?)
在 90% 的场景下,官方推荐受控组件。但在某些特定场景,非受控组件是更好的选择。
1. 必须使用“受控组件”的场景
只要涉及到**“即时反馈”或“逻辑联动”**,必须受控:
- 即时校验(Instant Validation): 用户每打一个字,你就提示“密码太短”。
- 条件禁用(Conditional Disable): 输入框 A 有值时,按钮 B 才能点击。
- 输入格式化(Input Masking): 比如信用卡号,用户输入
123456,你自动显示为1234 56。 - 多个输入框联动: 输入汇率,自动计算兑换后的金额。
2. 必须/建议使用“非受控组件”的场景
- 文件上传 (
<input type="file" />):- 这是最特殊的。因为安全原因,JS 无法通过脚本设置文件路径,只能由用户操作 DOM 选取。文件输入框在 React 中始终是非受控的。
- 集成非 React 第三方库:
- 如果你要在 React 里用一个 jQuery 插件、D3.js 或者 Google Maps,这些库通常直接操作 DOM,这时候用非受控组件配合 Ref 是最佳实践。
- 极致性能优化(大型表单):
- 这是高级技巧: 如果一个表单有 500 个输入框,全是受控组件。用户在第 1 个框输入 1 个字,会导致整个父组件重渲染(虽然有 Diff,但 JS 执行开销依然存在),打字可能会卡顿。
- 解决方案: 使用非受控组件,或者使用类似 React Hook Form 这样的库。
- 注:React Hook Form 的底层原理就是“非受控组件”,它通过 Ref 绑定来避免输入时的重渲染,从而极大提升了大型表单的性能。
五、 总结
- 新手/普通场景: 闭眼选 受控组件。它符合 React 的数据流思想,好维护,好控制。
- 高手/特殊场景: 如果你遇到了文件上传,或者发现表单输入卡顿(性能瓶颈),请切换到 非受控组件(或使用 React Hook Form)。