R15第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

受控与非受控输入

受控与非受控输入 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

表单输入有两种实现路线:让 React 每次都管着输入框(受控),或让 DOM 自己记着、用到时再读(非受控)。这一课把两种都写一遍,弄清各自的读取时机、重置方式和适用边界,特别是 defaultValue 只在首次挂载生效这个经典坑。

问题场景

课程资料页有个「重置」按钮,实现是给输入框的 value 重新赋初始值:

<input defaultValue={initial.nickname} />
<button onClick={() => setNickname(initial.nickname)}>重置</button>

点了没反应。输入框的显示内容归 DOM 管,defaultValue 只在首次挂载时生效,之后改这个 prop 对界面毫无影响——React 不知道、也不关心 DOM 里的当前值。

核心解释

受控输入:React state 是唯一事实源。 value 接 state,onChange 回写 state,形成闭环:

const [nickname, setNickname] = useState(initial.nickname);
<input value={nickname} onChange={(e) => setNickname(e.target.value)} />

输入框每次变化都会触发渲染,任何时刻都能读到最新值:实时校验、字数统计、提交按钮禁用(昵称为空不可提交)都建立在「随时可读」之上。重置也简单——setNickname("") 一行。

非受控输入:DOM 自己持有值,React 不参与。 只给 defaultValue 设初值,需要值时用 ref 在读取的那一刻去 DOM 拿:

const remarkRef = useRef(null);
<input defaultValue={initial.remark} ref={remarkRef} />
const value = remarkRef.current.value; // 提交时才读

打字过程不触发任何渲染,性能好;代价是「写不到、读不准」——渲染期间读 ref 的值没有意义,读取时机被推迟到了事件回调里。

怎么选? 默认受控:需要实时响应、需要程序化修改值、需要多输入联动,全都指向受控。非受控适合「一次性的、只关心提交结果的」输入:文件选择(<input type="file"> 只能非受控)、性能敏感的超大表单、集成第三方 DOM 组件。同一条经验:要不要在输入过程中用到这个值?要,就受控。

重置非受控输入有一条技巧:给它换个 key,强制 React 卸载重建 DOM 节点,defaultValue 重新生效——「改 key 重挂载」比命令式操作 DOM 更符合 React 的习惯。

示例拆解

实验区把同一个「昵称 + 备注」表单做成两栏对照,关键点如下:

  • 受控栏:输入时下方实时显示字数和预览(每次输入都触发渲染);「重置」按钮一行 setState 即可恢复初始值;
  • 非受控栏:输入时下方毫无反应(不渲染);「提交」按钮用 ref 一次性读出当前值显示出来——读取时机在事件回调里;
  • 非受控栏有两个重置按钮:一个是坏示范(重设 defaultValue prop,点了没反应),一个是好示范(setKey(n => n + 1) 换 key 强制重挂载,值真的回到初始);
  • 「程序改值」按钮在受控栏直接改 state,输入框跟着变;非受控栏没有对应操作——React 改不了它。

验证方式:两边各输入几个字,观察渲染反应差异;再各点一次重置,对比效果。

动手任务

打开本课实验区,完成三个小改动:

  1. 给受控栏加「昵称为空时提交按钮禁用」,验证实时校验为什么只有受控做得到。
  2. 给非受控栏的提交结果加长度上限校验(读值后判断),体会「提交时才校验」的滞后。
  3. 在非受控栏试试 value={stateValue}(受控写法)但漏掉 onChange,观察输入框锁死——理解警告「You provided a value prop to a form field without an onChange handler」。

常见错误

  • 给 defaultValue 之后又改它的值:只在首次挂载生效,后续修改无效;重置要用受控赋值或换 key。
  • 受控输入漏写 onChange:输入框变成只读,控制台有警告;value 与 onChange 必须成对。
  • 渲染期间读 ref 取输入值:ref.current.value 在渲染函数里读到的时机不确定,读取要放在事件回调里。
  • 一个表单里两套思路混用还互相依赖:受控字段参与实时校验、非受控字段参与校验结果,两边的读取时机不同步;同一张表单里先统一受控,确有性能压力再局部改非受控。

检查与复盘

完成下方检查题确认理解。自查清单:

  • 能说出两种方式的读取时机:受控随时可读,非受控在事件回调里读
  • 能解释 defaultValue 的「首次挂载生效」语义,以及换 key 重置的原理
  • 选型时先问「输入过程中要不要用这个值」,默认受控

参考资料

动手实验
动手实验
预览
点击「运行」在隔离沙箱中预览
检查题共 1 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1多选中等(多选,全部选对才算正确)

关于受控与非受控组件,下列说法正确的有?(多选)

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...