React当中受控组件与非受控组件的区别

在 React 中,受控组件 (Controlled Components)非受控组件 (Uncontrolled Components) 是两种管理表单数据的方式,主要区别在于表单数据由谁来管理(状态来源)以及如何获取用户输入的值

1. 受控组件 (Controlled Components)

核心思想: 表单元素的值(value)由 React 组件的 state 管理。React 完全控制表单元素的状态。

实现方式:

  • 将表单元素(如 <input>, <textarea>, <select>)的 value 属性绑定到组件的 state
  • 通过 onChange 事件处理函数监听用户的输入,并在该函数中更新 state
  • 由于 valuestate 控制,任何输入都会先触发 onChange,更新 state,然后 state 的变化会重新渲染组件,从而更新 value,形成一个闭环。

优点:

  • 数据流清晰: 所有表单数据都集中管理在 React 的 state 中,便于调试和维护。
  • 易于验证和转换: 可以在 onChange 处理函数中轻松地对输入进行实时验证、格式化或转换。
  • 易于重置或预填充: 只需修改 state,表单值就会随之改变。
  • 符合 React 单向数据流原则。

缺点:

  • 代码量稍多: 需要为每个需要控制的表单元素编写 stateonChange 处理函数。
  • 性能开销: 每次输入都触发 state 更新和组件重新渲染(虽然 React 通常能优化得很好)。

示例:

jsx 复制代码
import React, { useState } from 'react';

function ControlledInput() {
  const [name, setName] = useState('');

  const handleChange = (event) => {
    setName(event.target.value); // 更新 state
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    console.log('提交的姓名:', name);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        姓名:
        <input
          type="text"
          value={name} // value 由 state 控制
          onChange={handleChange} // 通过事件更新 state
        />
      </label>
      <button type="submit">提交</button>
    </form>
  );
}

2. 非受控组件 (Uncontrolled Components)

核心思想: 表单元素的值由元素自身(DOM)管理,而不是由 React 的 state 管理。React 不直接控制其值。

实现方式:

  • 使用 ref 通过 useRef Hook 或 createRef() 创建一个 ref,并将其附加到表单元素上。
  • 获取值: 当需要获取表单数据时(例如在提交时),通过 ref.current 访问 DOM 元素,然后读取其 value 属性。
  • 默认值: 可以使用 defaultValue(或 defaultChecked 等)属性来设置初始值,之后这个值就由 DOM 自己管理了。

优点:

  • 更接近原生 HTML: 对于简单的表单或需要与非 React 代码集成时,使用起来更直接。
  • 减少代码量: 对于不需要实时响应输入的场景,代码更简洁。
  • 性能: 避免了频繁的 state 更新和重新渲染(仅在需要时读取值)。

缺点:

  • 数据流不清晰: 表单数据分散在 DOM 中,不如 state 集中管理。
  • 难以实时验证/转换: 无法像受控组件那样在 onChange 中轻松处理。
  • 难以动态控制: 从 React 代码中改变非受控组件的值比较麻烦(需要操作 ref)。
  • 与 React 哲学不完全一致: 将状态管理交给了 DOM。

示例:

jsx 复制代码
import React, { useRef } from 'react';

function UncontrolledInput() {
  const nameInputRef = useRef(null); // 创建 ref

  const handleSubmit = (event) => {
    event.preventDefault();
    // 通过 ref 获取 DOM 元素的值
    console.log('提交的姓名:', nameInputRef.current.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        姓名:
        <input
          type="text"
          ref={nameInputRef} // 将 ref 附加到 input
          defaultValue="初始值" // 设置默认值(只在首次渲染时有效)
        />
      </label>
      <button type="submit">提交</button>
    </form>
  );
}

总结对比表

特性 受控组件 (Controlled) 非受控组件 (Uncontrolled)
状态管理 React state DOM 自身
值来源 value 属性绑定到 state 通过 ref 读取 DOM 的 value
初始值 state 的初始值 defaultValue 属性
获取值时机 任何时刻(通过 state 提交或需要时(通过 ref
实时响应 容易(onChange 困难
数据流 清晰,单向(React) 分散(DOM)
代码复杂度 相对较高(需 stateonChange 相对较低
适用场景 需要实时验证、格式化、动态控制、复杂表单逻辑 简单表单、文件上传、集成非 React 代码、性能敏感且无需实时控制的场景

一般建议: 在大多数情况下,优先使用受控组件 ,因为它更符合 React 的设计理念,数据流更清晰,功能更强大。只有在特定需求(如处理文件输入 input[type="file"] 必须是非受控的,或者性能要求极高且逻辑简单)时才考虑使用非受控组件。

相关推荐
小陈工6 分钟前
2026年4月7日技术资讯洞察:下一代数据库融合、AI基础设施竞赛与异步编程实战
开发语言·前端·数据库·人工智能·python
Cobyte15 分钟前
3.响应式系统基础:从发布订阅模式的角度理解 Vue2 的数据响应式原理
前端·javascript·vue.js
竹林81818 分钟前
从零到一:在React前端中集成The Graph查询Uniswap V3池数据实战
前端·javascript
Mintopia25 分钟前
别再迷信"优化":大多数性能问题根本不在代码里
前端
倾颜26 分钟前
接入 MCP,不一定要先平台化:一次 AI Runtime 的实战取舍
前端·后端·mcp
军军君0128 分钟前
Three.js基础功能学习十八:智能黑板实现实例五
前端·javascript·vue.js·3d·typescript·前端框架·threejs
恋猫de小郭28 分钟前
Android 上为什么主题字体对 Flutter 不生效,对 Compose 生效?Flutter 中文字体问题修复
android·前端·flutter
Moment30 分钟前
AI全栈入门指南:一文搞清楚NestJs 中的 Controller 和路由
前端·javascript·后端
禅思院31 分钟前
前端架构演进:基于AST的常量模块自动化迁移实践
前端·vue.js·前端框架
程序员马晓博31 分钟前
前端并发治理:从 Token 刷新聊起,一个 Promise 就够了
前端·javascript