表单数据怎么管?受控组件和非受控组件,看完就懂怎么选

97 阅读3分钟

写表单的时候,你是不是会纠结:输入框里的内容该存在哪里?是用useState存起来,还是等提交的时候再拿?其实这对应着 React 里的两个概念 ——“受控组件” 和 “非受控组件”。

这俩名字听起来挺唬人,其实本质很简单:受控组件就是 “实时监控” 输入,非受控组件就是 “用的时候再看” 。今天用最通俗的话,结合具体例子聊聊它们的区别、用法和什么时候该选哪个

一、先看例子:两种表单的不同写法

先看一段代码(来自提供的示例),感受下两种组件的直观区别:

1. 受控组件(ControlledInput)

function ControlledInput({ onSubmit }) {
  // 用useState存输入框的值
  const [value, setValue] = useState('');
  const [error, setError] = useState('');

  // 输入变化时,实时更新state
  const handleChange = (e) => {
    setValue(e.target.value);
    // 实时验证:输入长度不够就提示错误
    if (e.target.value.length < 6) {
      setError('输入不能少于6个字符');
    } else {
      setError('');
    }
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    onSubmit(value); // 直接从state拿值
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={value} //  value和state绑定
        onChange={handleChange} // 输入变化就更新state
      />
      {error && <p>{error}</p>} {/* 实时显示错误 */}
      <button type="submit">提交</button>
    </form>
  );
}

image.png 核心特点:输入框的值完全由 React 的 state 控制,输入变化会实时更新 state,提交时从 state 里取数据。

2. 非受控组件(UncontrolledInput)

function UncontrolledInput({ onSubmit }) {
  // 用useRef创建一个“引用”,关联到输入框
  const inputRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    // 提交时,通过ref拿到输入框的值
    const value = inputRef.current.value;
    onSubmit(value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        ref={inputRef} // 用ref关联输入框
      />
      <button type="submit">提交</button>
    </form>
  );
}

核心特点:输入框的值由 DOM 自身管理,React 不实时跟踪,提交时通过 ref 直接从 DOM 元素上获取数据。

二、核心区别:数据 “谁来管”?啥时候 “更”?

1. 受控组件:数据由 React “实时管”

  • 数据存在哪里:存在useState里(React 的状态管理);
  • 怎么更新:输入框一变化(onChange事件),就立刻用setValue更新 state;
  • 怎么拿值:直接从 state 里取(比如value变量);
  • 特点:输入框的value和 state “绑死”,state 变了,输入框显示就变;输入框变了,state 也跟着变(双向绑定)。

就像你妈做饭时 “实时盯着锅”:菜一糊就关火,盐少了立刻加 —— 全程监控,随时调整。

2. 非受控组件:数据由 DOM “自己管”

  • 数据存在哪里:存在 DOM 元素里(输入框自己保存);
  • 怎么更新:输入时 React 不管,数据直接存在浏览器的 DOM 里;
  • 怎么拿值:需要的时候(比如提交),通过ref从 DOM 里 “捞” 出来;
  • 特点:React 不实时监控,只在需要时 “问” DOM 要数据。

就像你妈把菜放进锅里,盖上盖子不管了,等出锅时才开盖看看 —— 平时不管,用的时候再检查。

三、什么时候用受控?什么时候用非受控?

选受控组件的场景:

  • 需要实时反馈:比如输入长度验证(像例子里 “少于 6 个字符就提示”)、实时计算(如购物车输入数量后立刻显示总价);
  • 需要限制输入:比如只能输入数字、密码显示星号(type="password");
  • 表单联动:比如 “选择省份后,城市下拉框自动更新”—— 一个输入影响另一个输入。

简单说:只要需要 “实时处理输入”,就用受控组件

选非受控组件的场景:

  • 简单表单:比如登录框,只需要在点击 “登录” 时拿一次账号密码;
  • 文件上传<input type="file"必须用非受控(因为它的value是只读的,只能通过ref拿);
  • 性能敏感场景:比如超大表单(几百个输入框),实时更新 state 可能有点性能开销,不如提交时再拿值。

简单说:只在提交时用一次数据,且不需要实时处理,就用非受控组件

总结:一句话记住两者区别

  • 受控组件:state “管” 数据,输入时实时更,适合实时处理;
  • 非受控组件:DOM “管” 数据,提交时再拿值,适合简单场景。