受控组件 vs 非受控组件
聊聊表单数据到底归谁管
先说人话:这两种模式的区别就是——表单的值到底归 React 管,还是归 DOM 自己管。
就这一句话,核心区别就在这。
受控组件:React 当家作主
什么是受控组件?
就是表单的值由 React 的 state 完全控制。你输入一个字,state 变一次,组件重新渲染一次。
怎么写?
function ControlledInput() {
const [value, setValue] = useState("");
const handleChange = (event) => {
setValue(event.target.value); // 每次输入都更新 state
};
return (
<input
type="text"
value={value} // 值由 state 决定
onChange={handleChange} // 每次变化都更新 state
/>
);
}
工作流程:
- 用户打字 -> 触发
onChange
onChange 调用 setState 更新 state
- state 变了 -> 组件重新渲染
- 重新渲染后,
value={value} 把新值塞回输入框
有啥优点?
- 数据随时可控:state 就是"唯一真相",随时可以拿到最新值
- 验证/格式化方便:每次输入都能拦截,想咋处理输入都行
- 编程式控制:想重置输入?
setValue("") 就行;想设值?setValue("hello") 就搞定
- 符合 React 套路:数据自上而下流动,清楚明白
有啥缺点?
每次输入都触发 setState、重新渲染,性能上可能有点浪费——不过对于大多数场景,这点开销可以忽略不计。
非受控组件:DOM 自己管自己
什么是非受控组件?
表单的值由 DOM 自己管理,React 不管。React 想要值的时候,通过 ref 去 DOM 节点"拉取"。
怎么写?
function UncontrolledInput() {
const inputRef = useRef(null);
const handleSubmit = () => {
// 需要用值的时候,去 DOM 里拿
alert("输入的值是: " + inputRef.current.value);
};
return (
<>
{/* 不需要 value 属性,用 defaultValue 设置初始值 */}
<input type="text" ref={inputRef} defaultValue="初始值" />
<button onClick={handleSubmit}>提交</button>
</>
);
}
有啥优点?
- 更像原生 HTML:写法跟普通表单一样
- 代码更简单:不用写 onChange,不用写 setState
- 性能更好:每次输入不触发 React 渲染
- 集成第三方库方便:有些库要自己操作 DOM,用 ref 直接拿到节点,很顺滑
有啥缺点?
- 数据不可控:你想在用户输入过程中做验证?难
- 想重置值?麻烦:得手动操作 DOM,违背了 React 的套路
- 不知道当前值是啥:除非你去 ref 里拿
到底该用哪个?
大多数情况下,用受控组件。
React 官方推荐受控组件,因为数据可控、逻辑清晰、符合 React 思想。
用受控组件的情况:
- 需要即时验证输入(密码强度、格式校验)
- 需要根据输入动态控制其他 UI(输入为空时禁用提交按钮)
- 需要强制格式化(比如自动转大写、格式化手机号)
- 表单逻辑复杂,多个字段相互依赖
- 需要编程式重置或赋值
用非受控组件的情况:
- 表单极其简单,只需要在提交时拿一次值
- 要接第三方库(图片上传、富文本编辑器),它们自己要操作 DOM
- 输入极其频繁(比如实时绘图中选颜色),性能实在敏感
总结
| 受控组件 | 非受控组件 |
|---|
| 数据归谁管 | React state | DOM 自己 |
| 写法 | value={state} + onChange | ref + defaultValue |
| 性能 | 每次输入都渲染 | 更轻量 |
| 控制力 | 随时可控 | 难 |
| 适用场景 | 大多数场景 | 简单表单、集成第三方库 |
记住一句话:React 能管的就用受控组件,React 管不了的或者不想管的,就用非受控组件。