受控组件与非受控组件:React 表单处理的两种方式
在 React 开发中,表单的处理是构建交互式应用的重要部分。React 提供了两种主要的方式来管理表单数据:受控组件和非受控组件。这两种方式各有优劣,适用于不同的场景。
一、受控组件(Controlled Components)
受控组件是指其值由 React 的状态(state)控制的表单元素。这意味着表单元素的值始终与 React 组件的 state 保持同步。每当用户输入发生变化时,都会触发一个事件处理器来更新 state,从而更新表单元素的值。
特点
-
表单值由 React 状态管理,完全控制输入值,所以可以对输入进行格式化、限制、验证等操作。还将输入值与其他状态或组件共享。
-
每次输入变化都会触发
onChange事件,更新状态。由于每次输入都会触发setState,导致组件重新渲染,所以性能差。 但可以使用防抖节流来优化性能,这里适合使用防抖。
-
允许对用户输入进行实时验证和操作,这样更加适合动态表单,比如根据用户输入显示不同的表单字段。
示例
import { useState, useRef } from "react";
import "./App.css";
function App() {
const handleSubmit = (value) => {
console.log(value);
};
return (
<>
<Controlled onSubmit={handleSubmit} />
<UnControlled onSubmit={handleSubmit} />
</>
);
}
function Controlled({ onSubmit }) {
const [value, setValue] = useState("");
const [valid, setValid] = useState(true);
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(value);
};
return (
<>
<form onSubmit={handleSubmit}>
<label htmlFor="controlled-input">受控组件</label>
<input
id="controlled-input"
type="text"
value={value}
required
onChange={(e) => {
setValid(e.target.value.length > 5);
setValue(e.target.value);
}}
/>
<button type="submit">提交</button>
<br />
{!valid && <span style={{ color: "red" }}>输入错误</span>}
</form>
</>
);
}
export default App;
二、非受控组件(Uncontrolled Components)
非受控组件是指其值由 DOM 自身管理的表单元素。与受控组件不同,非受控组件不会将输入值绑定到 React 的 state,而是通过 ref 来获取 DOM 元素的值。
特点
- 表单值由 DOM 控制并使用
ref来获取输入的值。所以对于简单的表单提交,无需维护状态。更加适合文件上传,因为文件输入(<input type="file" />)必须通过 DOM 获取值。 - 不需要为每个输入变化编写
onChange处理函数。所以对于大型表单,可以减少频繁的状态更新。
示例
function UnControlled({ onSubmit }) {
const inputRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(inputRef.current.value);
};
return (
<>
<form onSubmit={handleSubmit}>
<label htmlFor="uncontrolled-input">非受控组件</label>
<input id="uncontrolled-input" type="text" ref={inputRef} />
<button type="submit">提交</button>
</form>
</>
);
}
三、受控组件与非受控组件的对比
| 特性 | 受控组件 | 非受控组件 |
|---|---|---|
| 数据来源 | React State | DOM |
| 实时控制 | ✅ 可以对输入进行实时处理 | ❌ 不易控制输入 |
| 初始值设置 | 简单,通过 state 设置 | 需要使用 defaultValue |
| 文件上传支持 | ❌ 不推荐 | ✅ 推荐 |
| 性能 | 对大型表单可能稍慢 | 更轻量,适合简单场景 |
| 适用场景 | 需要验证、动态表单、多输入联动 | 简单表单、文件上传、快速原型开发 |
四、如何选择?
选择受控组件
- 需要实时验证或格式化输入。
- 表单数据需要与其他组件共享或影响 UI。
- 表单结构复杂,涉及多个字段联动。
选择非受控组件
- 表单逻辑简单,只需在提交时获取数据。
- 需要处理文件上传。
- 希望减少状态管理的复杂度。