受控组件与非受控组件:谁握有「真源」?
知识背景
表单输入的值由谁驱动,决定了你是受控还是非受控。
- 受控:输入值存在 React state里,
onChange里setState,真源(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.valuedefaultValue 只设初始值,后续由用户自由改 DOM。适合:一次性读取、或与不好同步到 React 的控件配合。
3. 文件输入
<input type="file" /> 在 React 里通常是只读的受控困难场景,常用非受控 + ref 或受控文件名等派生状态。
4. 「半受控」反模式
父组件用 key 强制重挂载子组件来「重置」、或子组件内外各维护一份 value不同步,都会难维护。约定:同一字段只有一个真源。
总结
- 受控:state 驱动,适合复杂交互与校验。
- 非受控:DOM 驱动,适合简单场景与特殊输入类型。
- 选型本质是 真源在哪;混用时要明确数据流方向,避免双份 state。