受控组件与非受控组件:React 表单处理的两种方式

100 阅读3分钟

受控组件与非受控组件:React 表单处理的两种方式

在 React 开发中,表单的处理是构建交互式应用的重要部分。React 提供了两种主要的方式来管理表单数据:受控组件非受控组件。这两种方式各有优劣,适用于不同的场景。

一、受控组件(Controlled Components)

受控组件是指其值由 React 的状态(state)控制的表单元素。这意味着表单元素的值始终与 React 组件的 state 保持同步。每当用户输入发生变化时,都会触发一个事件处理器来更新 state,从而更新表单元素的值。

特点

  • 表单值由 React 状态管理,完全控制输入值,所以可以对输入进行格式化、限制、验证等操作。还将输入值与其他状态或组件共享。

  • 每次输入变化都会触发 onChange 事件,更新状态。

    由于每次输入都会触发setState,导致组件重新渲染,所以性能差。 但可以使用防抖节流来优化性能,这里适合使用防抖。

  • 允许对用户输入进行实时验证和操作,这样更加适合动态表单,比如根据用户输入显示不同的表单字段。

示例

import { useState, useRef } from "react";

import "./App.css";

function App() {
  const handleSubmit = (value) => {
    console.log(value);
  };

  return (
    <>
      <Controlled onSubmit={handleSubmit} />
      <UnControlled onSubmit={handleSubmit} />
    </>
  );
}

function Controlled({ onSubmit }) {
  const [value, setValue] = useState("");
  const [valid, setValid] = useState(true);

  const handleSubmit = (e) => {
    e.preventDefault();
    onSubmit(value);
  };

  return (
    <>
      <form onSubmit={handleSubmit}>
        <label htmlFor="controlled-input">受控组件</label>
        <input
          id="controlled-input"
          type="text"
          value={value}
          required
          onChange={(e) => {
            setValid(e.target.value.length > 5);
            setValue(e.target.value);
          }}
        />
        <button type="submit">提交</button>
        <br />

        {!valid && <span style={{ color: "red" }}>输入错误</span>}
      </form>
    </>
  );
}
export default App;

受控.gif

二、非受控组件(Uncontrolled Components)

非受控组件是指其值由 DOM 自身管理的表单元素。与受控组件不同,非受控组件不会将输入值绑定到 React 的 state,而是通过 ref 来获取 DOM 元素的值。

特点

  • 表单值由 DOM 控制并使用 ref 来获取输入的值。所以对于简单的表单提交,无需维护状态。更加适合文件上传,因为文件输入(<input type="file" />)必须通过 DOM 获取值。
  • 不需要为每个输入变化编写 onChange 处理函数。所以对于大型表单,可以减少频繁的状态更新。

示例

function UnControlled({ onSubmit }) {
  const inputRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    onSubmit(inputRef.current.value);
  };

  return (
    <>
      <form onSubmit={handleSubmit}>
        <label htmlFor="uncontrolled-input">非受控组件</label>
        <input id="uncontrolled-input" type="text" ref={inputRef} />
        <button type="submit">提交</button>
      </form>
    </>
  );
}

非受控.gif

三、受控组件与非受控组件的对比

特性受控组件非受控组件
数据来源React StateDOM
实时控制✅ 可以对输入进行实时处理❌ 不易控制输入
初始值设置简单,通过 state 设置需要使用 defaultValue
文件上传支持❌ 不推荐✅ 推荐
性能对大型表单可能稍慢更轻量,适合简单场景
适用场景需要验证、动态表单、多输入联动简单表单、文件上传、快速原型开发

四、如何选择?

选择受控组件

  • 需要实时验证或格式化输入。
  • 表单数据需要与其他组件共享或影响 UI。
  • 表单结构复杂,涉及多个字段联动。

选择非受控组件

  • 表单逻辑简单,只需在提交时获取数据。
  • 需要处理文件上传。
  • 希望减少状态管理的复杂度。