受控组件与非受控组件详解(React 视角)
受控组件与非受控组件详解(React 视角)
“受控组件(Controlled Component)/ 非受控组件(Uncontrolled Component)”是 React 表单开发中的基础概念,也是面试高频题。理解它们的区别,能帮助你在表单、输入框、富文本、文件上传等场景做出正确选型,并避免状态错乱、性能抖动等问题。
本文以 React 为主(因为“受控/非受控”在 React 语境中最典型),同时也会补充一些通用思路。
一、先用一句话理解
- 受控组件:表单值由 React state 驱动,
value由 state 提供,onChange更新 state。 - 非受控组件:表单值由 DOM 自己维护,React 不用 state 实时保存值,需要时通过
ref读取。
二、受控组件(Controlled)
1. 基本写法
1 | |
关键点:
value永远来自 state- 输入框变化时触发
onChange,再更新 state - state 更新后重新渲染,
value变更回写到 input
2. 受控组件的优点
- 单一数据源(Single Source of Truth):值始终在 React state 中,逻辑更可控。
- 易于做校验与格式化:例如输入时限制数字、自动 trim、实时校验提示。
- 易于联动:多个字段互相影响(比如省市区联动、禁用/显示条件)更自然。
- 提交更简单:提交时直接使用 state,无需去 DOM 取值。
3. 受控组件的常见坑
(1)忘记写 onChange 导致输入框不可编辑
1 | |
React 会认为这是“只读输入”,用户输入不会生效(控制权在你手里,你却没更新)。
(2)性能:大量字段/高频输入导致频繁 re-render
受控组件每次输入都会 setState → re-render。对简单表单通常没问题,但在以下场景需要注意:
- 超长列表中的输入框(如表格编辑)
- 富文本/高频输入的复杂组件
- 渲染成本较高的父组件树
优化思路:
- 将 state 下沉到更小的组件,减少影响范围
- 使用
React.memo、拆分组件 - 对昂贵计算使用
useMemo - 对“提交时才需要值”的字段考虑非受控或混合策略
(3)受控与非受控切换警告
当一个 input 一开始是 undefined(非受控),后面变成字符串(受控),会出现警告:
A component is changing an uncontrolled input to be controlled…
解决方式:
- 初始化 state 为
""而不是undefined/null
1 | |
三、非受控组件(Uncontrolled)
1. 基本写法(ref 读取)
1 | |
关键点:
- 使用
defaultValue(初始化值),而不是value - 之后输入框内容由 DOM 自己维护
- 需要值时,通过
ref.current.value读取
2. 非受控组件的优点
- 更少的渲染:输入过程不必每次 setState,减少 re-render。
- 更贴近原生表单:尤其适合“最终提交时读取一次值”的简单场景。
- 适配第三方组件:有些第三方输入类组件更容易以非受控方式集成(视具体库而定)。
3. 非受控组件的缺点
- 不利于实时校验与联动:值不在 state 中,做联动需要额外监听或手动读取。
- 数据流分散:值在 DOM,其他业务状态在 React,容易出现“两个世界”的同步问题。
- 更难做统一表单管理:例如“统一收集所有字段值、批量校验、统一重置”等。
四、受控 vs 非受控:对比与选型建议
| 维度 | 受控组件 | 非受控组件 |
|---|---|---|
| 数据源 | React state | DOM 自身 |
| 更新时机 | 每次输入都更新 state | 需要时用 ref 读取 |
| 校验/格式化 | 很方便(实时) | 需要额外处理 |
| 联动逻辑 | 更自然 | 更麻烦 |
| 性能 | 大量输入可能带来 re-render | 通常更轻 |
| 工程可控性 | 更强 | 更接近原生、但可控性弱 |
选型建议(实战)
- 业务表单(登录/注册/编辑页):优先受控(利于校验、联动、提交)。
- 极简单表单、只需提交读取一次:可以用非受控(减少样板代码)。
- 文件上传
<input type="file">:更常见是非受控(浏览器安全限制,value 不可随意设置)。 - 富文本编辑器/复杂输入控件:常见是“混合策略”(内部非受控,外层在某些时机同步到 state)。
五、进阶:混合策略(表单里很常见)
很多真实项目不是“纯受控/纯非受控”,而是混合:
- 输入过程中非受控,避免高频 re-render
- 失焦(onBlur)或提交时把值同步到 state / 表单模型
示例(onBlur 同步):
1 | |
这种策略适合:
- 输入非常频繁,但不需要实时联动
- 需要在某些关键节点(失焦/提交)进入 React 数据流
六、如何“重置”表单?
1. 受控组件重置
直接重置 state:
1 | |
2. 非受控组件重置
有两种常见方式:
- 手动改 DOM:
1 | |
- 或利用 key 让组件重新挂载(重置 defaultValue/内部状态):
1 | |
七、总结
- 受控组件:数据在 React state 中,适合绝大多数业务表单(校验、联动、提交清晰)。
- 非受控组件:数据在 DOM 中,适合简单场景或性能敏感输入(提交时读取、少渲染)。
- 混合策略:在复杂表单/高频输入场景很常见(输入不频繁同步,关键节点再同步)。
理解受控/非受控的本质后,你在选择表单方案时就不会只停留在“背定义”,而是能根据业务复杂度、性能与可维护性做出合理决策。
受控组件与非受控组件详解(React 视角)
https://sunjc.vip/2025/06/08/受控组件与非受控组件详解/