React 受控组件与非受控组件的区别
React 受控组件与非受控组件的区别
一、核心概念
-
受控组件(Controlled Components)
- 数据管理:表单元素(如
input
、textarea
、select
)的值由 React 的state
完全控制。 - 更新机制:通过
onChange
事件监听用户输入,实时更新state
,再通过value
属性将state
的值回传给表单元素。 - 示例:
function ControlledForm() { const [value, setValue] = useState(""); const handleChange = (e) => setValue(e.target.value); return <input type="text" value={value} onChange={handleChange} />; }
- 数据管理:表单元素(如
-
非受控组件(Uncontrolled Components)
- 数据管理:表单元素的值由 DOM 自身管理,React 通过
ref
直接访问 DOM 元素的值。 - 更新机制:仅在需要时(如提交表单)通过
ref
获取当前值,无需实时更新state
。 - 示例:
function UncontrolledForm() { const inputRef = useRef(null); const handleSubmit = () => console.log(inputRef.current.value); return ( <> <input type="text" ref={inputRef} defaultValue="初始值" /> <button onClick={handleSubmit}>提交</button> </> ); }
- 数据管理:表单元素的值由 DOM 自身管理,React 通过
二、核心区别
特性 | 受控组件 | 非受控组件 |
---|---|---|
数据控制 | 由 React state 完全控制 |
由 DOM 管理,通过 ref 获取值 |
更新方式 | 实时更新(onChange 事件) |
按需获取(如提交时) |
代码复杂度 | 较高(需定义 state 和事件处理) |
较低(无需实时更新逻辑) |
适用场景 | 实时验证、动态表单交互 | 一次性提交、集成非 React 代码 |
表单默认值 | 通过 value 属性设置 |
通过 defaultValue 或 defaultChecked 设置 |
三、使用场景对比
-
受控组件的典型场景
- 实时输入验证:在用户输入时即时校验格式(如密码强度)。
- 动态表单联动:一个表单字段的变化影响另一个字段(如选择国家后填充城市列表)。
- 强制格式输入:自动格式化输入内容(如电话号码
123-456-7890
)。
-
非受控组件的典型场景
- 文件上传:
<input type="file">
必须是非受控组件(浏览器安全限制)。 - 集成第三方库:如与 jQuery 插件结合,直接操作 DOM。
- 性能敏感场景:避免频繁
state
更新导致的重渲染(如表单元素极多)。
- 文件上传:
四、代码实现对比
-
受控组件示例(输入实时反馈)
function EmailInput() { const [email, setEmail] = useState(""); const [isValid, setIsValid] = useState(false); const handleChange = (e) => { const value = e.target.value; setEmail(value); setIsValid(/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)); }; return ( <div> <input type="email" value={email} onChange={handleChange} /> {!isValid && <p>请输入有效的邮箱地址</p>} </div> ); }
-
非受控组件示例(提交时获取值)
function SearchForm() { const searchRef = useRef(null); const handleSearch = () => { const keyword = searchRef.current.value; fetchResults(keyword); }; return ( <div> <input type="text" ref={searchRef} defaultValue="React" /> <button onClick={handleSearch}>搜索</button> </div> ); }
五、注意事项
-
受控组件的常见问题
- 输入卡顿:在大型表单中频繁更新
state
可能导致性能问题(可用debounce
优化)。 - 遗漏
onChange
:若未绑定onChange
事件,输入框会变为只读。// ❌ 错误:无法输入 <input value={value} /> // ✅ 正确:绑定 onChange <input value={value} onChange={(e) => setValue(e.target.value)} />
- 输入卡顿:在大型表单中频繁更新
-
非受控组件的常见问题
- 默认值更新无效:
defaultValue
只在初始渲染生效,后续state
变化不影响输入框。 - 失去 React 数据流优势:难以实现动态交互(如实时禁用提交按钮)。
- 默认值更新无效:
六、如何选择?
- 优先受控组件:大多数场景适用,符合 React 单向数据流设计,便于管理复杂交互。
- 考虑非受控组件:性能敏感、文件上传、或需直接操作 DOM 的特殊场景。
七、总结
决策因素 | 受控组件 | 非受控组件 |
---|---|---|
数据实时性 | ✅ 需要实时同步数据 | ❌ 仅在特定时机获取数据 |
代码维护性 | ✅ 便于集中管理状态和逻辑 | ❌ 逻辑分散,难以追踪 |
性能优化 | ❌ 频繁更新可能影响性能 | ✅ 减少不必要的重渲染 |
表单复杂度 | ✅ 适合动态、交互复杂的表单 | ❌ 适合简单、一次性提交的表单 |