前言
在 React 开发中,处理表单(Form)是我们最常见的交互场景之一。无论是登录注册,还是复杂的配置页面,都离不开 input 框的使用。
在 React 的设计哲学中,表单组件主要分为两大流派:受控组件和 非受控组件。它们各有千秋,理解它们的区别和适用场景,能帮我们在未来的开发中做出更优雅的技术选型。
一、受控组件
所谓“受控”,是指 input 框的 value 属性不再由 DOM 自身维护,而是完全被 React 组件的 State(状态) 所控制。这也是 React 官方推荐的“唯一数据源”模式。
1. 实现步骤
要实现一个受控组件,通常需要以下四步闭环:
- 声明状态:在组件中使用 useState 声明一个变量来存储表单的值。
- 绑定值:将状态数据赋值给 input 的 value 属性。
- 监听变化:绑定 onChange 事件,监听用户的输入行为。
- 更新状态:在事件处理程序中,拿到最新的值并调用 setState 更新状态。
2. 代码示例
import { useState } from 'react';
export default function ControlledInput() {
// 1. 声明状态,并给予初始值
const [message, setMessage] = useState('大家好');
function changeHandler(e) {
// 4. 将文本框的最新值同步给 state
// 注意:这里 e.target.value 获取的是用户刚刚输入后的最新值
setMessage(e.target.value);
// 提示:setState 是异步的,紧接着打印 message 可能还是旧值,这是正常现象
}
return (
<div>
{/* 2. & 3. 绑定 value 和 onChange */}
<input type="text" value={message} onChange={changeHandler}/>
<p>当前状态值: {message}</p>
</div>
)
}
3. 核心原理解析
为什么必须绑定 onChange?
如果你只设置了 value={message} 而不绑定 onChange,你会发现输入框变成了“只读”状态。这是因为 React 的渲染机制决定了视图是状态的函数。如果 State 没有改变,React 就会强制 input 渲染旧的 value 值。
只有通过 onChange 触发状态更新,React 重新渲染组件,input 的 value 才会变成新的值。这就是 “数据驱动视图”。
二、非受控组件
非受控组件更像传统的 HTML 表单。数据主要存储在 DOM 节点中,而不是 React 的 State 中。如果你需要获取值,必须“主动”去 DOM 里拿。
1. 实现方式
非受控组件通常使用 useRef 钩子来直接操作 DOM。
2. 代码示例
import { useRef } from 'react';
export default function UncontrolledInput() {
// 1. 创建一个 ref 对象
const inputRef = useRef(null);
function login() {
// 2. 在需要的时候(比如点击按钮),通过 ref.current 访问 DOM 节点获取值
console.log("用户输入的内容是:", inputRef.current.value);
}
return (
<div>
{/* 绑定 ref */}
<input type="text" ref={inputRef}/>
<button onClick={login}>登录</button>
</div>
)
}
3. 适用场景
非受控组件虽然不是 React 的“主流”,但在某些场景下非常有用:
- 文件上传
<input type="file" />:文件输入通常是只读的,必须由 DOM 处理。 - 集成第三方库:当使用非 React 编写的底层 DOM 库时。
- 简单表单:不需要即时验证,只需要在提交瞬间获取值的场景。
三、巅峰对决:如何选择?
为了帮你更好地做决定,我们将两者的特点整理如下:
| 特性 | 受控组件 (Controlled) | 非受控组件 (Uncontrolled) |
|---|---|---|
| 数据来源 | React State (组件状态) | DOM (节点本身) |
| 数据获取 | 实时获取 (onChange) | 只有在需要时获取 (通过 ref) |
| 实时验证 | 容易 (如:输入时检查格式) | 较麻烦 |
| 代码量 | 相对较多 (需要写处理函数) | 相对较少 |
| 推荐指数 | ⭐⭐⭐⭐⭐ (官方推荐) | ⭐⭐⭐ (特定场景使用) |
结语
- 如果你需要即时反馈(例如:输入密码时显示强度、限制输入字符长度、根据输入内容动态禁用按钮),请务必使用 受控组件。
- 如果你只是想做一个简单的表单,或者处理 文件上传,不想写太多的 state 逻辑,非受控组件 是一个轻量级的选择。