Skip to content

受控组件与非受控组件:谁握有「真源」?

知识背景

表单输入的值由谁驱动,决定了你是受控还是非受控

  • 受控:输入值存在 React state里,onChangesetState真源(single source of truth)在 React
  • 非受控:值主要在 DOM 里,用 ref 在提交时读一次或通过原生事件取值,真源在 DOM
    React 文档不再强调对立,而是让你按场景选择:简单表单、文件输入、与第三方库集成时常用非受控;需要即时校验、联动、禁用按钮逻辑时多用受控。

知识详解与通俗解释

1. 受控示例

jsx
const [value, setValue] = useState('');
<input value={value} onChange={(e) => setValue(e.target.value)} />

优点:输入即 state,易做联动、校验、重置setValue('') 即可)。缺点:每个按键可能触发重渲染,超大表单需拆分或节流。

2. 非受控 + defaultValue

jsx
const ref = useRef(null);
<input defaultValue="hi" ref={ref} />
// 读取:ref.current.value

defaultValue 只设初始值,后续由用户自由改 DOM。适合:一次性读取、或与不好同步到 React 的控件配合。

3. 文件输入

<input type="file" /> 在 React 里通常是只读的受控困难场景,常用非受控 + ref 或受控文件名等派生状态。

4. 「半受控」反模式

父组件用 key 强制重挂载子组件来「重置」、或子组件内外各维护一份 value不同步,都会难维护。约定:同一字段只有一个真源


总结

  • 受控:state 驱动,适合复杂交互与校验。
  • 非受控:DOM 驱动,适合简单场景与特殊输入类型。
  • 选型本质是 真源在哪;混用时要明确数据流方向,避免双份 state。