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>
);
}
这里没有 value 和 onChange。用户输入直接存放在 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 更便捷。
- 你希望减少组件的重渲染次数,或有意让代码更贴近原生行为。
一个常见的误解是:非受控组件无法设置初始值。其实可以通过
defaultValue和defaultChecked来定义初始状态,它们只作用于组件首次挂载时。
常见问题与误区
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];
// 上传逻辑...
};