React 受控 vs 非受控组件:核心概念解析

核心概念

简单来说,区别就在于"谁负责存储并管理表单数据"。


1. 受控组件

在受控组件中,React 组件的 state 是表单数据的"唯一真相源"

  • 如何工作 :你需要为每个表单元素(如 input, textarea, select)设置两个属性:

    1. value:将其设置为 React state 中的一个值。
    2. onChange:设置一个事件处理函数,当用户输入时,用事件对象(e.target.value)中的新值来更新 state。
  • 数据流 :数据流是单向的state -> value属性 -> 输入框显示的值 用户输入 -> onChange 事件 -> 更新 state -> 重新渲染 -> 输入框显示新的值。

  • 代码示例

    jsx 复制代码
    import { useState } from 'react';
    
    function ControlledComponent() {
      const [inputValue, setInputValue] = useState('');
    
      const handleChange = (event) => {
        setInputValue(event.target.value);
      };
    
      const handleSubmit = (event) => {
        event.preventDefault();
        alert('提交的名字是: ' + inputValue);
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <label>
            名字:
            <input
              type="text"
              value={inputValue} // input 的值由 state 控制
              onChange={handleChange} // 一旦输入就更新 state
            />
          </label>
          <button type="submit">提交</button>
        </form>
      );
    }
  • 优点

    • 即时验证/处理 :你可以直接在 onChange 处理函数中对用户的每一次输入进行操作,比如实时验证格式、即时搜索提示、禁用按钮等。
    • 完全控制:你可以以编程方式改变输入框的值(例如,通过重置表单的 state 来清空所有输入框)。
    • 符合 React 设计哲学:数据流清晰,易于理解和调试。
  • 缺点

    • 代码量稍多:需要为每个字段编写事件处理函数。
    • 潜在性能问题:如果表单非常庞大,每次击键都可能导致重新渲染,但通常对于现代浏览器和 React 的优化来说,这很少成为实际问题。

2. 非受控组件

在非受控组件中,表单数据由 DOM 本身处理 。你不需要用 state 来存储每个输入的值,而是使用 ref 来在需要时(例如提交表单时)从 DOM 节点中获取值。

  • 如何工作 :你不为 input 设置 value 属性(或设置为 undefined)。你给 input 一个 ref,然后通过 ref.current.value 来访问它的值。

  • 数据流 :数据流是传统式的,用户输入直接显示在输入框里,React 不干预这个过程,只在需要时去"拉取"数据。

  • 代码示例

    jsx 复制代码
    import { useRef } from 'react';
    
    function UncontrolledComponent() {
      const inputRef = useRef(null);
    
      const handleSubmit = (event) => {
        event.preventDefault();
        // 在提交时,通过 ref 获取 DOM 元素的值
        alert('提交的名字是: ' + inputRef.current.value);
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <label>
            名字:
            <input
              type="text"
              ref={inputRef} // 将 ref 关联到 input 元素
              // 没有 value 和 onChange 属性
            />
          </label>
          <button type="submit">提交</button>
        </form>
      );
    }
  • 默认值 :如果你想给输入框一个初始值,可以使用 defaultValue 属性(对于复选框则是 defaultChecked),而不是 value

    jsx 复制代码
    <input type="text" defaultValue="Hello" ref={inputRef} />
  • 优点

    • 代码简单:对于简单的表单,代码更少,更接近原生 HTML。
    • 性能:对于巨型表单,可能因为更少的 state 更新和重新渲染而有一丝性能优势(但通常不明显)。
  • 缺点

    • 即时反馈困难:难以实现实时验证、条件禁用等需要根据当前输入值动态变化的 UI。
    • 不够"React":数据流不清晰,需要直接操作 DOM,这与 React 声明式的理念有些背道而驰。

对比总结

特性 受控组件 非受控组件
数据管理 React state DOM 自身
推荐度 官方推荐,更常用 在特定场景下使用
值控制 value 属性 ref
事件处理 必需 (onChange 等) 可选
实时操作 容易(验证、禁用等) 困难
表单提交 从 state 中获取数据 从 ref 中获取数据
默认值 通过 state 设置(如 useState('') 使用 defaultValue 属性

如何选择?

  • 大多数情况下,请使用受控组件。这是 React 的标准实践,让你对数据有完全的控制权,能轻松实现各种交互逻辑。

  • 在以下情况,可以考虑非受控组件

    1. 一次性获取:你只需要在提交时获取一下值,中间过程不需要任何验证或控制。
    2. 集成非 React 库 :当你需要集成一个第三方库(如 jQuery 插件),而这个库需要直接操作 DOM 时,使用 ref 是必要的。
    3. 文件输入<input type="file"> 始终是一个非受控组件,因为它的值是只读的,只能由用户设置,而不能通过编程方式设置。
    4. 性能极端敏感:面对一个包含大量输入字段的表单(如数据表格),每次击键都更新 state 可能导致性能问题,这时非受控组件可能是一个优化选择。

最终建议:默认优先考虑受控组件。除非你有非常明确的理由(如上所述)要使用非受控组件,否则坚持使用受控组件会让你的应用更可控、更健壮。

相关推荐
Yvonne爱编码7 分钟前
后端编程开发路径:从入门到精通的系统性探索
java·前端·后端·python·sql·go
boonya1 小时前
Redisson原理与面试问题解析
面试·职场和发展·redission·分布式中间件框架
GIS之路1 小时前
GDAL 读取遥感影像数据
前端
在未来等你1 小时前
Elasticsearch面试精讲 Day 16:索引性能优化策略
大数据·分布式·elasticsearch·搜索引擎·面试
Leo来编程2 小时前
七层网络协议-面试
网络·网络协议·面试
IT_陈寒2 小时前
Spring Boot 3.2 新特性全解析:这5个性能优化点让你的应用提速50%!
前端·人工智能·后端
携欢2 小时前
PortSwigger靶场之Stored DOM XSS通关秘籍
前端·xss
LDM>W<2 小时前
Electron下载失败
前端·javascript·electron
EndingCoder3 小时前
Electron 新特性:2025 版本更新解读
前端·javascript·缓存·electron·前端框架·node.js·桌面端
BillKu3 小时前
Vue3 中使用 DOMPurify 对渲染动态 HTML 进行安全净化处理
前端·安全·html