React 受控组件与非受控组件
在 React 中,受控组件和非受控组件是两种不同的表单处理方式。
受控组件(Controlled Components)
受控组件的值由 React 组件的状态(state)控制。这意味着表单数据的变化会触发状态更新,而状态的更新又会反映到表单上。受控组件允许你通过 React 的状态管理来控制表单的行为,包括验证、阻止和修改用户输入等。
非受控组件(Uncontrolled Components)
非受控组件的值不由 React 的状态直接控制,而是由 DOM 本身来维护。在这种模式下,你可以使用 ref 来直接从 DOM 节点中获取表单数据。非受控组件适用于那些不需要即时响应状态变化或者更倾向于使用传统 DOM 操作的场景。
区别
受控组件 | 非受控组件 | |
---|---|---|
数据源 | 数据来源于 React 的状态 | 数据来源于 DOM |
数据流向 | 双向的,用户输入会更新状态,状态的更新会改变 UI | 向的,从 DOM 读取数据 |
控制方式 | 通过事件处理器(如 onChange)来控制用户的输入 | 通常不直接控制用户的输入 |
适用场景 | 适合于需要即时反馈和复杂交互的场景 | 适合于简单的表单或者性能敏感的应用 |
受控组件
在 React 中,受控组件是一种特殊的组件,其值由 React 的 state 控制。这种机制允许开发者通过设置特定的 state 并在用户输入时更新这个 state,从而实现对输入字段如文本框、单选按钮、下拉列表等的精确控制。
受控组件的工作原理如下:
- 初始化 State:首先,在组件的构造函数中初始化 state,为每个表单元素设定一个初始值。
- 渲染表单元素:在 render 方法中,渲染表单元素并使用 value 属性将其值绑定到 state 中对应的值。
- 事件处理:对于需要用户输入的表单元素,通常会添加一个 onChange 事件监听器。当用户输入时,会触发这个事件,并调用一个事件处理函数。
- 更新 State:在事件处理函数中,使用事件对象 e.target.value 获取用户输入的新值,并使用 setState 更新 state。这样,组件的 state 就会随着用户的输入实时更新。
- 提交表单:当表单被提交时,可以在提交事件的处理函数中访问当前的 state,以获取所有表单字段的值。
受控组件示例:
import { ChangeEvent, useState } from 'react'
function Controlled() {
const [name, setName] = useState('')
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setName(e.target.value)
}
return (
<>
<p>您输入的名字是:{name}</p>
<input type="text" value={name} onChange={handleChange} />
</>
)
}
export default Controlled
优势
- 一致性:受控组件确保了组件的显示状态和 React 的 state 保持一致,这有助于避免状态不一致的问题。
- 易用性:受控组件使得表单处理更加直观和易于管理,因为所有的状态更新都通过 React 的 state 进行。
- 可预测性:由于所有的状态更改都是显式的,因此受控组件的行为是可预测的,这对于调试和维护是有利的。
- 即时反馈:受控组件允许开发者在用户输入时立即进行验证和格式化,从而提供即时反馈。
- 灵活性:受控组件可以轻松地与其他 React 特性(如条件渲染、状态提升等)结合使用,提供了很高的灵活性。
缺点
- 复杂性增加:对于复杂的表单,可能需要编写大量的状态更新逻辑,这会增加代码的复杂性。
- 性能开销:每次用户输入都会导致组件的重新渲染,如果处理不当,可能会导致性能问题。
- 学习曲线:对于不熟悉 React 的开发者来说,理解和正确使用受控组件可能需要一段时间的学习和实践。
- 代码冗余:对于多个相似表单元素,可能需要重复编写相似的 onChange 处理函数,导致代码冗余。
- 异步问题:在某些情况下,如使用第三方库或处理异步操作时,受控组件可能需要进行额外的处理以确保正确的行为。
受控组件在大多数情况下都是首选的表单处理方式,因为它们提供了更好的控制和可预测性。然而,在处理特别复杂的表单或者对性能有极高要求的情况下,可能需要考虑其他策略,比如使用非受控组件或者第三方表单库。
非受控组件
在 React 中,非受控组件是指其值不受 React 控制的组件。与受控组件不同,非受控组件的值由 DOM 自身管理,而不是通过 React 的状态进行管理。这种类型的组件适用于那些不需要即时响应状态变化或者更倾向于使用传统 DOM 操作的场景。
非受控组件示例:
import { useRef, useState } from 'react'
function UnControlled() {
const ref = useRef<HTMLInputElement>(null)
const [name, setName] = useState('')
const handleSubmit = () => {
setName(ref.current?.value || '')
}
return (
<>
<p>您输入的名字是:{name}</p>
<input type="text" ref={ref} />
<button onClick={handleSubmit}>提交</button>
</>
)
}
export default UnControlled
非受控组件的使用场景
- 简单的表单:对于一些简单的表单,你可能不需要 React 的状态管理功能,此时使用非受控组件可以减少代码的复杂性。
- 性能优化:在某些情况下,使用非受控组件可以避免不必要的渲染,从而提高应用的性能。
- 第三方库:当你需要与某些第三方库(如富文本编辑器)集成时,这些库可能有自己的方式来管理状态,这时使用非受控组件会更加方便。
优势
- 简单易用:非受控组件的代码通常比受控组件更简单,因为它们不需要在 React 的状态中维护表单值。
- 性能优化:在某些情况下,非受控组件可以避免不必要的渲染,因为它们的值不是由 React 的状态直接控制的。
- 与第三方库兼容:当需要与不支持 React 受控模式的第三方库(如某些富文本编辑器)集成时,非受控组件可能是更好的选择。
- 即时 DOM 访问:非受控组件可以直接访问 DOM 元素,这在需要即时访问 DOM 属性或方法时非常有用。
缺点
- 状态不一致:非受控组件的值由 DOM 维护,不与 React 的状态同步,这可能导致状态不一致的问题。
- 难以追踪和调试:由于非受控组件的值不在 React 的状态中,因此在调试时可能难以追踪值的来源和变化。
- 缺乏控制:非受控组件不允许 React 在每次状态变化时执行验证或转换逻辑,这限制了开发者对表单输入的控制能力。
- 不利于单元测试:由于非受控组件的值不是由 React 的状态管理的,因此在对组件进行单元测试时可能更加困难。
- 安全风险:如果非受控组件用于敏感信息的输入,而没有适当的清理和验证,可能存在安全风险。