news 2026/4/12 7:06:24

受控组件 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/4/10 21:04:52

Python构建AI Agent自主智能体系统

核心要点 环境建模与状态表示 动作空间与策略选择 奖励机制与反馈循环 训练与优化 1. 环境建模与状态表示 环境建模是AI Agent系统的基础,它决定了Agent如何感知和理解外部世界。状态表示则是将环境中的信息转化为Agent可以处理的形式。 原理 环境建模:定义环境的规则和状态…

作者头像 李华
网站建设 2026/4/2 5:10:12

每天一道面试题之架构篇|低延迟实时排行榜系统架构设计

面试官直接问道&#xff1a;"如果要设计一个支持百万用户实时竞技的游戏排行榜&#xff0c;如何保证低延迟和高并发&#xff1f;" 一、开篇&#xff1a;实时排行榜的核心挑战 想象一下&#xff1a;王者荣耀巅峰赛最后10秒&#xff0c;百万玩家同时刷新排名&#xff0…

作者头像 李华
网站建设 2026/4/2 7:57:20

基于 GEE 利用 ERA5-LAND 数据集自动提取全球径流数据:地表 + 地下径流总和(mm)完整提取方案

目录 一、区域定义与地图初始化 二、数据集加载 三、年份筛选与数据计算 四、数据导出到 Google Drive 五、代码关键特性与注意事项 六、运行结果 若觉得代码对您的研究 / 项目有帮助&#xff0c;欢迎点击打赏支持&#xff01;需要完整代码的朋友&#xff0c;打赏后可在后…

作者头像 李华
网站建设 2026/3/27 19:00:45

Linux SSH配置密钥文件及免密登录

操作 1、生成私钥文件 ssh-keygen -t rsa -f id_pem_systemssh-keygen -p -m PEM -f ./id_pem_system2、加入authorized_keys文件 cat id_pem_system.pub >> authorized_keys配置A机器免密登录B机器 1、获取B机器私钥 2、登录A机器&#xff0c;并且上传B机器私钥到以下目…

作者头像 李华
网站建设 2026/4/10 15:53:23

样式复用多态

Styles https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-style-0000001473856690-V2 如果每个组件的样式都需要单独设置&#xff0c;在开发过程中会出现大量代码在进行重复样式设置&#xff0c;虽然可以复制粘贴&#xff0c;但为了代码简洁性和后续方…

作者头像 李华