React中的受控组件与非受控组件
React 表单里受控组件与非受控组件的区别,以及它们各自适合的场景。
#tech / dev / frame
#resource / react
#type / concept
#status / growing
[!info] related notes
React中的受控组件与非受控组件
这组概念最常出现在表单场景里,核心差异是“数据的真实来源到底是谁”。
一句话定义
- 受控组件:表单值由 React state 驱动
- 非受控组件:表单值主要保存在 DOM 自己内部
受控组件
const [value, setValue] = useState('')
<input value={value} onChange={(e) => setValue(e.target.value)} />
- 数据流更清晰
- 更容易做校验、联动、提交前处理
- 更适合复杂表单
为什么 React 社区更常强调它
因为它更符合 React 的单向数据流:
- 输入变化进入 state
- state 再驱动 UI
所以复杂表单里,受控组件通常更容易和校验、禁用、联动、提交状态统一起来。
非受控组件
const inputRef = useRef<HTMLInputElement | null>(null)
<input ref={inputRef} defaultValue="" />
- 更接近原生表单用法
- 简单场景代码更少
- 常见于快速接第三方表单库或低交互输入场景
一个典型读取方式
function Form() {
const inputRef = useRef<HTMLInputElement | null>(null)
function handleSubmit() {
console.log(inputRef.current?.value)
}
return (
<>
<input ref={inputRef} defaultValue="" />
<button onClick={handleSubmit}>submit</button>
</>
)
}
怎么选更实用
- 表单逻辑复杂、需要实时校验:优先受控
- 只是简单拿最终值:非受控也可以
一个常见误区
不要把“非受控”理解成落后方案,也不要把“受控”理解成永远更高级。
真正的判断标准是:
- 这份输入值是否要持续进入 React 数据流
- 是否有复杂联动和校验
- 是否只是提交时读一次就够
最短记忆方式
受控看 state,非受控看 DOM。
面试要点
来自 react-controlled-vs-uncontrolled-components-interview-question 的面试视角整理。
一句话回答
受控组件把表单值交给 React state 管理,非受控组件则主要让 DOM 自己保存当前值,React 只在需要时去读取。
最稳的回答主线
- 受控组件更适合复杂表单、实时校验、联动逻辑
- 非受控组件更适合简单输入或和原生表单行为更接近的场景
- 复杂业务里大多数表单还是更偏向受控组件
一个更完整的面试表达
可以这样答:
受控和非受控的本质区别是“当前值到底由谁控制”。受控组件把表单值交给 React state 管,输入变化会进入 React 数据流;非受控组件主要由 DOM 自己保存值,React 只在需要时通过 ref 或表单提交去读取。
所以复杂业务表单通常更偏向受控,因为它更容易做实时校验、联动、禁用控制和统一提交逻辑。
一个典型例子
受控组件:
const [value, setValue] = useState('')
<input value={value} onChange={e => setValue(e.target.value)} />
非受控组件:
const inputRef = useRef<HTMLInputElement | null>(null)
<input ref={inputRef} defaultValue="hello" />
什么时候非受控更合适
- 表单很简单
- 只想在提交时一次性读取值
- 要和原生表单能力或第三方非 React 组件配合
一个加分点
不是说受控永远更高级,而是它更适合复杂交互;非受控也不是“旧方案”,它在简单场景下反而更直接。
最短记忆方式
谁管值,谁就是“控制方”。