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"] 必须是非受控的,或者性能要求极高且逻辑简单)时才考虑使用非受控组件。

相关推荐
kyriewen1 小时前
今年裁了16万技术人——但字节前端岗反而涨了23%
前端·javascript·面试
用户059540174461 小时前
把 AI Agent 记忆存储验证从 40 分钟手测压到 3 分钟,pytest + Docker 这套组合太顶了
前端·css
stereohomology1 小时前
MS Edge很多事的一点是把地址栏复制的地址自动转标题文本
前端·edge
imaol11 小时前
链表 -- 环链表
java·前端·链表
imaol12 小时前
链表 -- 双向链表
java·前端·链表
东风破_2 小时前
大前端手里的 Next.js:从 #root 到 SEO,一份 HTML 的两种命运
前端·后端
IT_陈寒2 小时前
Vue的v-for不听话?我被这个Key的坑整懵了
前端·人工智能·后端
王林不想说话3 小时前
ES6 到 ES2026 全面进阶指南:新特性、原理、示例与工程落地
前端·javascript·面试
用户43523713814933 小时前
🚀 Vue3 + TypeScript 学习笔记:从泛型 T 到 Promise<T>,终于搞懂了 keyof 和 typeof
前端
跟着群主去吃肉3 小时前
Cinemachine的应用-第三人称视角
前端·unity3d·游戏开发