文章目录

React 表单:受控组件与非受控组件

发布于 2026-04-17 13:16:52 · 浏览 13 次 · 评论 0 条

React 表单:受控组件与非受控组件

在 React 开发中,处理表单输入是核心交互之一。根据数据由谁(React State 还是 DOM)来持有“唯一真实数据源”,我们将表单组件分为受控组件和非受控组件。理解并正确使用这两种模式,能让你在处理复杂表单验证时游刃有余,或者在快速搭建简单表单时更加高效。


第一部分:受控组件

受控组件是 React 推荐的表单处理方式。在这种模式下,表单数据的“唯一真实数据源”是 React 组件内部的 State。这意味着输入框的值完全由 State 控制,用户的每一次输入都会触发 State 的更新,进而更新视图。

实现步骤

  1. 定义状态变量。
    在组件内部使用 useState Hook 定义一个变量,用于存储输入框的值。

  2. 绑定值属性。
    将输入框(如 <input>)的 value 属性设置为第一步定义的状态变量。

  3. 监听变化事件。
    为输入框添加 onChange 事件监听器。当用户输入时,该事件会触发。

  4. 更新状态。
    在事件处理函数中,通过 event.target.value 获取用户输入,并调用 State 更新函数来同步数据。

代码示例

import React, { useState } from 'react';

function ControlledForm() {
  // 1. 定义状态
  const [username, setUsername] = useState('');

  // 3. & 4. 监听变化并更新状态
  const handleChange = (event) => {
    setUsername(event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    // 提交时直接使用 State 中的数据
    console.log('提交的用户名:', username);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        用户名:
        {/* 2. 绑定 value */}
        <input 
          type="text" 
          value={username} 
          onChange={handleChange} 
        />
      </label>
      <button type="submit">提交</button>
    </form>
  );
}

数据流转逻辑

受控组件的数据流转形成一个闭环:用户输入触发事件,事件更新 State,State 更新触发重新渲染,重新渲染将新值回填到输入框。

graph LR A["用户输入"] -->|触发: onChange| B["更新 State"] B -->|触发: 重新渲染| C["Input: value 更新"] C -->|展示| A

核心特点与适用场景

  • 实时性:可以即时对输入进行验证(如限制字符长度、格式检查)。
  • 统一性:所有输入数据都集中在 State 中,方便传递给其他组件或处理。
  • 适用场景:需要进行动态验证、多输入联动(如确认密码)、或者有条件提交(如输入正确才亮起按钮)的复杂表单。

第二部分:非受控组件

非受控组件类似传统的 HTML 表单行为。表单数据由 DOM 元素本身管理,而不是由 React 组件的 State 控制。要获取输入的值,你需要使用 ref(引用)来“拉取” DOM 节点的数据。

实现步骤

  1. 创建引用。
    使用 useRef Hook 创建一个 ref 对象。

  2. 关联 DOM 节点。
    将创建的 ref 对象通过 ref 属性附加到 JSX 的输入框元素上。

  3. 获取数据。
    在需要数据的时候(通常是提交表单时),通过 ref.current.value 直接读取 DOM 中的值。

代码示例

import React, { useRef } from 'react';

function UncontrolledForm() {
  // 1. 创建 ref
  const inputRef = useRef(null);

  const handleSubmit = (event) => {
    event.preventDefault();
    // 3. 直接从 DOM 获取值
    const name = inputRef.current.value;
    console.log('提交的用户名:', name);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        用户名:
        {/* 2. 关联 ref */}
        <input 
          type="text" 
          defaultValue="初始值" 
          ref={inputRef} 
        />
      </label>
      <button type="submit">提交</button>
    </form>
  );
}

核心注意点

  • 使用 defaultValue:在非受控组件中,如果要设置初始值,必须使用 defaultValue 属性,而不是 value 属性(否则会变成只读)。
  • 数据读取时机:由于数据不随输入实时更新到 State,通常只在提交(onSubmit)时读取一次。

适用场景

  • 简单表单:如一次性登录框,无需实时验证。
  • 集成第三方库:当使用某些非 React 编写且直接操作 DOM 的库时。
  • 性能考量:在极其庞大的表单中,避免每次击键都触发 State 更新和重渲染(虽然现代 React 性能通常已足够,但在极端场景下非受控更优)。

第三部分:对比与选择

为了在实际开发中快速做出决策,可以参考下表对两种模式进行直观对比。

特性 受控组件 非受控组件
数据源 React State DOM
获取值方式 通过 State 变量 通过 ref.current.value
实时验证 支持(每输入一个字符都能触发) 不支持(通常在提交时验证)
代码量 较多(需编写 Handler 和 State) 较少(无需编写 Handler)
受控性 强(React 完全掌控 UI) 弱(依赖 DOM 默认行为)

决策逻辑指南

在开始编写代码前,判断以下条件:

  1. 检查是否需要对用户的每一次输入进行即时反馈(如“密码强度:弱”)?
    • 是:使用受控组件。
  2. 检查表单字段是否非常多,且不需要联动或即时验证?
    • 是:考虑非受控组件以减少渲染压力。
  3. 检查是否需要根据输入值动态控制其他 UI 元素(如输入正确时解锁按钮)?
    • 是:使用受控组件。
  4. 检查是否仅仅是简单的数据收集(如搜索框、基础登录)?
    • 是:使用非受控组件以快速完成开发。

混合使用策略

在一个复杂的 React 应用中,不需要非黑即白。你可以在同一个表单中混合使用这两种模式。例如,用户名和密码使用受控组件以便做实时长度验证和格式校验,而文件上传字段使用非受控组件(因为文件操作必须依赖 DOM 的 File 对象)。

function MixedForm() {
  const [text, setText] = useState(''); // 受控
  const fileInputRef = useRef(null);     // 非受控

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('文本:', text);
    console.log('文件:', fileInputRef.current.files[0]);
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* 受控输入 */}
      <input value={text} onChange={e => setText(e.target.value)} />

      {/* 非受控文件输入 */}
      <input type="file" ref={fileInputRef} />

      <button>提交</button>
    </form>
  );
}

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文