React 中表单受控组件和非受控组件

FreeGuideOnline 最新 2026-07-06

jsx import { useState } from 'react';

function ControlledForm() { const [name, setName] = useState('');

const handleSubmit = (e) => { e.preventDefault(); alert(提交的名字: ${name}); };

return ( 名字: <input type="text" value={name} onChange={(e) => setName(e.target.value)} /> 提交 ); }


在这个例子中,`<input>` 的值永远等于 `name` 这个 state。如果你在输入框中键入任何字符,`onChange` 会把最新值设置到 state,从而驱动界面更新。React 始终是表单数据的“唯一真实来源”。

**受控组件的特点:**
- 数据由 React state 管理,便于实时校验、动态禁用按钮等操作。
- 可以通过 `value` 属性设置默认值、格式化输入(如强制大写)。
- 每次按键都会触发重新渲染,在大型表单中需要注意性能(通常不是问题)。
- 代码更显式,适合需要严格控制的场景。

---

### 什么是非受控组件?

非受控组件的值由 **DOM 自身保管**,React 不追踪输入内容。你需要通过 `ref`(引用)直接从 DOM 节点读取当前值,而不是使用 state。

```jsx
import { useRef } from 'react';

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

  const handleSubmit = (e) => {
    e.preventDefault();
    alert(`提交的名字: ${inputRef.current.value}`);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        名字:
        <input type="text" ref={inputRef} />
      </label>
      <button type="submit">提交</button>
    </form>
  );
}

这里没有 valueonChange。用户输入直接存放在 DOM 的 <input> 元素中,提交时通过 ref.current.value 一次性读取。React 只在需要时才介入。

非受控组件的特点:

  • 代码更简洁,因为不需要为每个输入绑定 state 和处理函数。
  • 更接近原生 HTML 表单的工作方式。
  • 不触发频繁的重新渲染,技术上更“轻量”。
  • 不便于实时校验、条件渲染或动态格式化。

受控 vs 非受控:对比一览

对比维度 受控组件 非受控组件
数据归属 React state DOM 自身
更新方式 onChange + setState 通过 ref 在需要时读取
默认值设置 使用 value / checked 属性 使用 defaultValue / defaultChecked
即时校验 容易,每次输入变化都能响应 困难,通常只在提交时检查
条件渲染控制 方便,输入值变化可直接驱动 UI 不便,需要主动读取值再决定
渲染开销 每次输入都会重新渲染组件 输入变化不触发渲染
常用场景 复杂表单、实时搜索、联动输入 简单表单、文件上传、集成非 React 代码

如何选择?

没有一个规则能适用于所有情况,但下面的建议可以帮你快速决策。

优先使用受控组件,当:

  • 你需要即时校验(如密码强度实时显示)。
  • 表单字段相互依赖(一个下拉框的值决定另一个输入框的可用性)。
  • 需要动态禁用提交按钮,或者按条件显示错误信息。
  • 需要对用户输入进行格式化(例如电话号码自动添加分隔符)。

使用非受控组件更合适,当:

  • 表单非常简单,只需在提交时一次性获取所有值。
  • 需要处理文件输入(<input type="file">),因为它的值只能由用户设置,React 无法通过 value 控制。
  • 与第三方非 React 库集成时,直接操作 DOM 更便捷。
  • 你希望减少组件的重渲染次数,或有意让代码更贴近原生行为。

一个常见的误解是:非受控组件无法设置初始值。其实可以通过 defaultValuedefaultChecked 来定义初始状态,它们只作用于组件首次挂载时。


常见问题与误区

1. 受控组件必须提供 onChange,否则输入框无法编辑

如果你只写了 value={state} 但没有 onChange,React 会锁定这个输入框——因为 value 固定了,用户无法输入。控制台还会给出警告。

// ❌ 错误:只控制 value 却不处理变化
<input type="text" value={name} />

2. 非受控组件可以搭配 defaultValue 初始化

<input type="text" ref={inputRef} defaultValue="初始文本" />

这样组件渲染时会显示“初始文本”,用户之后可以自由修改。

3. 文件输入永远是非受控组件

因为安全限制,你无法用 JavaScript 设置 <input type="file"> 的值。所以处理文件上传时,必须使用 ref 或非受控方式。

const fileRef = useRef();
const handleUpload = () => {
  const file = fileRef.current.files[0];
  // 上传逻辑...
};