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 组件配合

一个加分点

不是说受控永远更高级,而是它更适合复杂交互;非受控也不是“旧方案”,它在简单场景下反而更直接。

最短记忆方式

谁管值,谁就是“控制方”。

创建于 2026/3/19 更新于 2026/5/27