news 2026/6/23 15:00:34

受控组件 vs 非受控组件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
受控组件 vs 非受控组件

核心区别

受控组件 (Controlled Components)

由 React 完全控制,表单数据由 React 组件的 state 管理。

非受控组件 (Uncontrolled Components)

由 DOM 自身管理,通过 ref 获取表单值。

详细对比

特性受控组件非受控组件
数据管理React stateDOM
值更新onChange事件 +setState用户直接输入
获取值state.valueref.current.value
初始化值value属性defaultValue属性
表单提交从 state 获取从 ref 获取
实时验证容易实现较难实现
即时反馈立即更新 UI需要手动触发

代码示例

1.受控组件示例

import React, { useState } from 'react'; const ControlledForm = () => { const [formData, setFormData] = useState({ name: '', email: '', age: '' }); const handleChange = (e) => { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); }; const handleSubmit = (e) => { e.preventDefault(); console.log('表单数据:', formData); }; return ( <form onSubmit={handleSubmit}> <div> <label>姓名:</label> <input type="text" name="name" value={formData.name} onChange={handleChange} /> </div> <div> <label>邮箱:</label> <input type="email" name="email" value={formData.email} onChange={handleChange} /> </div> <div> <label>年龄:</label> <input type="number" name="age" value={formData.age} onChange={handleChange} /> </div> <button type="submit">提交</button> </form> ); };

2.非受控组件示例

import React, { useRef } from 'react'; const UncontrolledForm = () => { const nameRef = useRef(null); const emailRef = useRef(null); const ageRef = useRef(null); const handleSubmit = (e) => { e.preventDefault(); const formData = { name: nameRef.current.value, email: emailRef.current.value, age: ageRef.current.value }; console.log('表单数据:', formData); }; return ( <form onSubmit={handleSubmit}> <div> <label>姓名:</label> <input type="text" name="name" ref={nameRef} defaultValue="" // 初始化值 /> </div> <div> <label>邮箱:</label> <input type="email" name="email" ref={emailRef} defaultValue="" /> </div> <div> <label>年龄:</label> <input type="number" name="age" ref={ageRef} defaultValue="" /> </div> <button type="submit">提交</button> </form> ); };

使用场景对比

适合使用受控组件的场景:

// 1. 需要实时验证 const ValidatedInput = () => { const [value, setValue] = useState(''); const [error, setError] = useState(''); const handleChange = (e) => { const val = e.target.value; setValue(val); // 实时验证 if (val.length < 3) { setError('至少需要3个字符'); } else { setError(''); } }; return ( <div> <input value={value} onChange={handleChange} style={{ borderColor: error ? 'red' : 'gray' }} /> {error && <div style={{ color: 'red' }}>{error}</div>} </div> ); }; // 2. 表单联动 const DependentFields = () => { const [country, setCountry] = useState(''); const [cities, setCities] = useState([]); const [selectedCity, setSelectedCity] = useState(''); const countries = { china: ['北京', '上海', '广州'], usa: ['纽约', '洛杉矶', '芝加哥'] }; const handleCountryChange = (e) => { const selected = e.target.value; setCountry(selected); setCities(countries[selected] || []); setSelectedCity(''); // 重置城市选择 }; return ( <div> <select value={country} onChange={handleCountryChange}> <option value="">选择国家</option> <option value="china">中国</option> <option value="usa">美国</option> </select> <select value={selectedCity} onChange={(e) => setSelectedCity(e.target.value)} disabled={!cities.length} > <option value="">选择城市</option> {cities.map(city => ( <option key={city} value={city}>{city}</option> ))} </select> </div> ); };

适合使用非受控组件的场景:

// 1. 文件上传 const FileUpload = () => { const fileRef = useRef(null); const handleSubmit = (e) => { e.preventDefault(); console.log('文件:', fileRef.current.files[0]); }; return ( <form onSubmit={handleSubmit}> <input type="file" ref={fileRef} /> <button type="submit">上传</button> </form> ); }; // 2. 简单表单,不需要实时验证 const SimpleForm = () => { const formRef = useRef(null); const handleSubmit = (e) => { e.preventDefault(); const formData = new FormData(formRef.current); const data = Object.fromEntries(formData); console.log(data); }; return ( <form ref={formRef} onSubmit={handleSubmit}> <input name="username" defaultValue="" /> <input name="password" type="password" defaultValue="" /> <button type="submit">登录</button> </form> ); }; // 3. 集成第三方库 const ThirdPartyIntegration = () => { const editorRef = useRef(null); useEffect(() => { // 初始化第三方富文本编辑器 editorRef.current = new ThirdPartyEditor('editor'); }, []); const getContent = () => { return editorRef.current.getContent(); }; return <div id="editor"></div>; };

混合使用模式

const HybridComponent = () => { const [search, setSearch] = useState(''); const fileRef = useRef(null); const handleSubmit = (e) => { e.preventDefault(); const formData = { searchTerm: search, // 来自受控输入 file: fileRef.current?.files[0] // 来自非受控输入 }; console.log('提交的数据:', formData); }; return ( <form onSubmit={handleSubmit}> {/* 受控输入:需要实时搜索建议 */} <input type="text" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="搜索..." /> {/* 非受控输入:文件上传 */} <input type="file" ref={fileRef} /> <button type="submit">提交</button> </form> ); };

性能考虑

// 性能优化示例:防抖处理 const OptimizedControlledInput = () => { const [value, setValue] = useState(''); const [displayValue, setDisplayValue] = useState(''); // 防抖函数 const debouncedSetDisplayValue = useMemo( () => debounce((val) => setDisplayValue(val), 300), [] ); const handleChange = (e) => { const val = e.target.value; setValue(val); // 立即更新 state debouncedSetDisplayValue(val); // 延迟更新显示值 }; // 清理防抖函数 useEffect(() => { return () => { debouncedSetDisplayValue.cancel(); }; }, [debouncedSetDisplayValue]); return ( <div> <input value={value} onChange={handleChange} /> <p>显示的值: {displayValue}</p> </div> ); };

最佳实践建议

优先使用受控组件,当:

  • 需要实时验证和反馈

  • 表单字段之间有依赖关系

  • 需要动态禁用/启用字段

  • 要实现复杂的表单逻辑

考虑使用非受控组件,当:

  • 处理文件上传

  • 集成第三方 DOM 库

  • 表单非常简单,不需要实时验证

  • 性能是关键考虑因素,且表单项非常多

React 18 的新特性

// useDeferredValue 优化受控组件性能 const DeferredInput = () => { const [value, setValue] = useState(''); const deferredValue = useDeferredValue(value); // 使用 deferredValue 进行昂贵的计算 const expensiveResult = useMemo(() => { // 昂贵的计算 return processValue(deferredValue); }, [deferredValue]); return ( <div> <input value={value} onChange={(e) => setValue(e.target.value)} /> <div>{expensiveResult}</div> </div> ); };

总结

决策因素选择受控组件选择非受控组件
表单复杂度
实时验证需求需要不需要
性能要求一般极高
代码可维护性
测试友好度

现代 React 开发中,推荐优先使用受控组件,因为它提供更好的可预测性和可维护性。只有在有明确性能需求或特殊场景下,才考虑使用非受控组件。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 12:11:31

根据您提供的 package.json 片段,涉及的 @vue/cli-plugin-babel 和 @vue/cli-service 版本为 ~4.2.0。以下是针对该版本的详细解决方案,结合相关依

根据您提供的 package.json 片段&#xff0c;涉及的 vue/cli-plugin-babel 和 vue/cli-service 版本为 ~4.2.0。以下是针对该版本的详细解决方案&#xff0c;结合相关依赖冲突问题&#xff1a;1. ‌核心依赖冲突处理‌‌问题‌&#xff1a;vue/cli-plugin-babel 和 vue/cli-ser…

作者头像 李华
网站建设 2026/6/23 3:05:01

electron-egg打包win7

node版本 v16.17.1 项目依赖版本"devDependencies": {"electron/rebuild": "^3.2.5","types/node": "16.18.88","debug": "^4.4.0","ee-bin": "^4.1.10","electron": &q…

作者头像 李华
网站建设 2026/6/23 7:37:06

8种网络故障分析及测试命令大全

对于网络工程师来说&#xff0c;在平常工作中&#xff0c;最基础的工作能力就是要保证网络顺畅不出错误。但实际上&#xff0c;网络随时都可能发生故障&#xff0c;影响正常工作&#xff0c;从而导致网络工程师总是要背很多黑锅&#xff1b;所以快速解决网络故障已经成为网工的…

作者头像 李华
网站建设 2026/6/22 23:26:38

Pythonselenium自动化测试实战项目

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快说明&#xff1a;本项目采用流程控制思想&#xff0c;未引用unittest&pytest等单元测试框架一、项目介绍目的测试某官方网站登录功能模块可以正常使用用例1.输入…

作者头像 李华
网站建设 2026/6/23 15:49:16

关于Comtos Linux (朱雀)主体源码的选择

关于Comtos Linux (朱雀) 主体源码的选择问&#xff1a; 为什么Comtos Linux 9 (朱雀)不以RHEL 9或 Rocky Linux 9或AlmaLinux 9的稳定的源码为主体&#xff0c;而使用滚动的CentOS Stream 9的源码为主体呢&#xff1f; 使用稳定的RHEL 9或Rocky Linux 9或AlmaLinux 9的源码为…

作者头像 李华