写表单的时候,你是不是会纠结:输入框里的内容该存在哪里?是用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>
);
}
核心特点:输入框的值完全由 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 “管” 数据,提交时再拿值,适合简单场景。