【react】受控组件 和 非受控组件

46 阅读4分钟
  • 受控组件:数据由 React 组件的 State 掌控。
  • 非受控组件:数据由 DOM 节点本身 掌控。

下面我把这两者掰开了揉碎了给你讲透。


一、 受控组件 (Controlled Components)

这是 React 官方推荐的“正统”写法。

1. 原理

React 组件的状态(state)是表单数据的唯一来源。

  • 显示: 输入框的值绑定到 state.value
  • 修改: 用户输入触发 onChange 事件 -> 调用 setState -> React 重新渲染 -> 输入框显示新值。

形成了一个闭环:Render -> User Input -> State Update -> Re-render

2. 代码示例

function ControlledInput() {
  const [text, setText] = useState('');

  const handleChange = (e) => {
    // 你可以在这里做任何事:大写转换、校验、过滤
    const upperText = e.target.value.toUpperCase();
    setText(upperText);
  };

  return (
    // value 绑定 state,onChange 负责更新 state
    <input value={text} onChange={handleChange} />
  );
}

3. 特点

  • 数据完全同步: React 永远知道输入框里现在是什么。
  • 控制力强: 你可以轻松截断用户输入(比如只能输数字)、实时校验(边打字边报错)。

二、 非受控组件 (Uncontrolled Components)

这更像传统的 HTML/JS 写法,React 只是作为一个“旁观者”。

1. 原理

React 不管输入框里显示什么,数据储存在 DOM 元素内部。

  • 显示: 使用 defaultValue 设置初始值(之后就不管了)。
  • 获取: 当你需要数据时(通常是点击提交按钮时),通过 Ref 去 DOM 里把数据“拉”出来。

2. 代码示例

function UncontrolledInput() {
  const inputRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    // 只有在这一刻,React 才去读取 DOM 的值
    alert('提交的值: ' + inputRef.current.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* 注意:这里用的是 defaultValue,没有 value 和 onChange */}
      <input type="text" defaultValue="初始值" ref={inputRef} />
      <button type="submit">提交</button>
    </form>
  );
}

3. 特点

  • 更接近原生: 操作方式和 jQuery 或原生 JS 操作 DOM 一样。
  • 性能较好: 输入字符不会触发 React 组件的重新渲染(Re-render)。

三、 核心区别 (对比总结)

特性受控组件 (Controlled)非受控组件 (Uncontrolled)
数据来源React StateDOM 自身
数据获取从 State 中直接读取通过 Ref 从 DOM 读取
实时性实时更新,每次按键都触发重渲染只有在需要时(如提交)才获取
值控制value 属性defaultValue 属性
性能较差(每次输入都 Render)较好(输入不触发 Render)
逻辑复杂度需编写 onChange 处理逻辑代码通常更少,但逻辑分散

四、 应用场景 (高级开发怎么选?)

在 90% 的场景下,官方推荐受控组件。但在某些特定场景,非受控组件是更好的选择。

1. 必须使用“受控组件”的场景

只要涉及到**“即时反馈”“逻辑联动”**,必须受控:

  • 即时校验(Instant Validation): 用户每打一个字,你就提示“密码太短”。
  • 条件禁用(Conditional Disable): 输入框 A 有值时,按钮 B 才能点击。
  • 输入格式化(Input Masking): 比如信用卡号,用户输入 123456,你自动显示为 1234 56
  • 多个输入框联动: 输入汇率,自动计算兑换后的金额。

2. 必须/建议使用“非受控组件”的场景

  • 文件上传 (<input type="file" />):
    • 这是最特殊的。因为安全原因,JS 无法通过脚本设置文件路径,只能由用户操作 DOM 选取。文件输入框在 React 中始终是非受控的。
  • 集成非 React 第三方库:
    • 如果你要在 React 里用一个 jQuery 插件、D3.js 或者 Google Maps,这些库通常直接操作 DOM,这时候用非受控组件配合 Ref 是最佳实践。
  • 极致性能优化(大型表单):
    • 这是高级技巧: 如果一个表单有 500 个输入框,全是受控组件。用户在第 1 个框输入 1 个字,会导致整个父组件重渲染(虽然有 Diff,但 JS 执行开销依然存在),打字可能会卡顿。
    • 解决方案: 使用非受控组件,或者使用类似 React Hook Form 这样的库。
    • 注:React Hook Form 的底层原理就是“非受控组件”,它通过 Ref 绑定来避免输入时的重渲染,从而极大提升了大型表单的性能。

五、 总结

  • 新手/普通场景: 闭眼选 受控组件。它符合 React 的数据流思想,好维护,好控制。
  • 高手/特殊场景: 如果你遇到了文件上传,或者发现表单输入卡顿(性能瓶颈),请切换到 非受控组件(或使用 React Hook Form)。