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 可能导致性能问题,这时非受控组件可能是一个优化选择。

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

相关推荐
折果1 小时前
如何在vue项目中封装自己的全局message组件?一步教会你!
前端·面试
不死鸟.亚历山大.狼崽子1 小时前
Syntax Error: Error: PostCSS received undefined instead of CSS string
前端·css·postcss
汪子熙1 小时前
Vite 极速时代的构建范式
前端·javascript
跟橙姐学代码1 小时前
一文读懂 Python 的 JSON 模块:从零到高手的进阶之路
前端·python
前端小巷子1 小时前
Vue3的渲染秘密:从同步批处理到异步微任务
前端·vue.js·面试
nightunderblackcat2 小时前
新手向:用FastAPI快速构建高性能Web服务
前端·fastapi
小码编匠2 小时前
物联网数据大屏开发效率翻倍:Vue + DataV + ECharts 的标准化模板库
前端·vue.js·echarts
欧阳天风3 小时前
分段渲染加载页面
前端·fcp
艾小码3 小时前
TypeScript在前端的实践:类型系统助力大型应用开发
前端·typescript
今禾3 小时前
前端工程化的范式革命:从 Webpack 的“全量打包”到 Vite 的“按需编译”
前端·webpack·vite