🎭 React 表单大戏:受控 vs 非受控,谁才是真正的“主角”?

25 阅读3分钟

“表单虽小,乾坤很大。”

在 React 的世界里,表单就像是一场舞台剧。有的演员(组件)严格按照剧本(状态)走位,一丝不苟;有的则自由发挥,临场即兴,只在关键时刻才汇报演出成果。
今天,我们就来聊聊这场精彩对决——受控组件 vs 非受控组件,看看谁更适合你的项目舞台!


🧙‍♂️ 什么是“受控”?什么是“非受控”?

🔒 受控组件(Controlled Component):状态说了算!

想象你是个霸道总裁,表单就是你的秘书。
你让她写什么,她就写什么;她想改一个字?必须先打报告(onChange),你批准(setState)后才能改。

const [value, setValue] = useState("");
<input 
  value={value}
  onChange={(e) => setValue(e.target.value)} 
/>
  • 特点:表单的值完全由 React 状态(state)控制。

  • 优点

    • 实时校验(比如密码强度提示)
    • 多字段联动(选城市自动更新区县)
    • 值始终可预测,调试友好
  • 缺点:代码略多,每个输入都要绑定 valueonChange

💡 适合场景:登录注册、复杂表单、需要实时反馈的交互。


🕶️ 非受控组件(Uncontrolled Component):DOM 自己管自己!

这次你放权了,让秘书自由发挥。
你只在最后提交时问一句:“你写了啥?”——她直接从草稿纸上念给你听(通过 ref 读取 DOM 值)。

const inputRef = useRef(null);
const handleSubmit = () => {
  console.log(inputRef.current.value); // 直接读 DOM!
};
<input ref={inputRef} />
  • 特点:表单数据由 DOM 自身管理,React 不干预。

  • 优点

    • 代码简洁,无需为每个字段写状态
    • 性能略优(少一次 re-render)
    • 特别适合一次性读取的场景(如评论、文件上传)
  • 缺点

    • 无法实时监听变化
    • 难以做动态校验或联动
    • “黑盒”操作,不利于测试和维护

💡 适合场景:简单评论框、搜索框、文件上传、性能敏感型应用。


🎭 实战对比:谁更胜一筹?

场景一:用户登录(需要校验 + 联动)

// ✅ 受控组件:完美胜任
const [form, setForm] = useState({ username: "", password: "" });

const handleChange = (e) => {
  setForm({ ...form, [e.target.name]: e.target.value });
};

return (
  <form>
    <input name="username" value={form.username} onChange={handleChange} />
    <input name="password" value={form.password} onChange={handleChange} />
    <button disabled={!form.username || !form.password}>登录</button>
  </form>
);

这里你能实时禁用按钮、高亮错误、甚至根据用户名自动填充邮箱——受控组件,YYDS!


场景二:用户评论(写完就提交,无需中间逻辑)

// ✅ 非受控组件:轻装上阵
const textareaRef = useRef();

const handleSubmit = () => {
  const comment = textareaRef.current.value;
  if (!comment) return alert("说点啥吧!");
  sendComment(comment);
};

return (
  <div>
    <textarea ref={textareaRef} placeholder="写下你的神评..." />
    <button onClick={handleSubmit}>发射!</button>
  </div>
);

没有状态、没有 onChange、没有烦恼。干净利落,像极了爱情(?)


⚠️ 常见误区 & 小贴士

  1. 拼写错误是魔鬼!
    textareaRef.current.vuale → 正确是 .value
    (别笑,这行代码来自真实事故现场 😭)
  2. 不要混用!
    同一个 input 既用 value={state} 又想用 ref.current.value
    React 会警告你:“你到底想让我控制还是不控制?”
  3. 文件上传只能是非受控!
    <input type="file" />value 是只读的,必须用 ref 获取文件。
  4. 性能真的差很多吗?
    对于普通表单,几乎没区别。除非你有成百上千个输入框(那你该考虑分页了😅)。

🎯 如何选择?一张表搞定!

场景推荐方案
需要实时校验/格式化✅ 受控
多字段联动(如省市区)✅ 受控
简单评论/搜索✅ 非受控
文件上传✅ 非受控
表单复杂度高✅ 受控(或用 Formik/Zod)
追求极致性能(且逻辑简单)✅ 非受控

🌟 终极建议:默认用受控,特殊场景用非受控

React 官方也推荐:尽可能使用受控组件
因为 React 的核心哲学就是——状态驱动 UI。让数据流清晰、可预测,才是长久之道。

但如果你只是想快速搞个评论框,又不想写一堆 useState,那非受控组件就是你的“快捷键”。


🎉 结语:表单无小事,选择需谨慎

受控组件像“直升机父母”,事无巨细都要管;
非受控组件像“放养式教育”,结果导向,过程自由。

没有绝对的好坏,只有合适的场景
下次写表单前,不妨问问自己:
“我需要实时掌控,还是只要最终答案?”

答案出来,选择自然清晰。