选受控还是非受控,核心看状态归属权:父组件统一管理为受控,子组件自行维护为非受控;受控用于表单联动、校验、重置等场景,非受控适用于独立轻量交互;混合模式需谨慎,须明确区分逻辑。

选受控还是非受控,核心看状态归属权:谁该拥有并更新这个值——是父组件统一管理(受控),还是子组件自己维护(非受控)。
受控组件:父组件说了算
组件的值完全由 props(如 value)驱动,变更必须通过回调(如 onChange)通知父组件,父组件再决定是否更新自己的 state。输入框、选择器、开关等需要与表单联动、校验、重置或跨组件同步时,必须用受控模式。
- 适合表单统一收集、提交前校验、动态禁用/重置、与其他字段联动(比如“是否启用”开关控制另一字段显隐)
- 避免在子组件内部用 useState 初始化 value,否则会触发 React 警告 “A component is changing an uncontrolled input to be controlled”
- 初始值来自 props 时,确保父组件传了有效值(哪怕为 '' 或 null),不要依赖子组件 fallback
非受控组件:子组件自己管
组件内部用 useState 或 useRef 管理值,父组件只传初始值(如 defaultValue),不干预后续变更。适用于独立操作、无需外部干预的场景,比如搜索框临时过滤、模态框内的草稿输入、纯展示型编辑器。
- 适合轻量交互、无需与全局状态耦合、或者初始化后就不再被父级干预的 UI 元素
- 若需读取当前值(例如点击“提交”时),优先用 ref + current.value,而非尝试监听变化
- 注意:非受控组件无法响应父组件对 defaultValue 的后续更新(React 不会重置 input 的 value),这点和受控有本质区别
混合策略:按需切换,但要谨慎
极少数场景下,组件需支持两种模式(比如第三方库封装)。可通过判断是否传入 value 来区分:有 value 且非 undefined → 受控;否则 → 非受控。但必须严格隔离逻辑,禁止在受控模式下写入内部 state,也禁止在非受控模式下监听 value 变化。
- 用 useEffect 同步 props 到内部 state 是危险信号——说明设计模糊,应明确归属
- 提供清晰文档:标明哪些 props 触发受控行为,哪些仅用于初始化
- 测试时需覆盖两种模式下的生命周期行为,尤其是重置、禁用、快速输入等边界情况
架构决策的关键线索
不是“哪个更高级”,而是“谁天然拥有这个数据”。用户填写的注册信息?属于业务逻辑层 → 受控。页面右上角的搜索关键词?属于视图局部状态 → 非受控。表单项嵌套在弹窗里,但提交后要合并到主表单?那它仍是受控,只是状态提升到了弹窗调用方。
- 数据流向越简单,可维护性越高:单向流动(props → 组件 → callback)优于双向同步
- 团队协作中,受控组件接口更稳定(props + callback 协议明确),非受控组件更易复用(无强依赖)
- 性能敏感场景(如高频输入),非受控可减少父组件 rerender;但若需实时校验,则受控配合 debounce 更可控

















