受控与非受控组件

React 中受控组件和非受控组件的区别与使用场景

问题

React 中受控组件和非受控组件有什么区别?分别在什么场景下使用?

解答

受控组件

表单元素的值由 React state 控制,每次输入都会触发状态更新。

import { useState } from 'react';

function ControlledForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    // 直接使用 state 中的值
    console.log({ name, email });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="姓名"
      />
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="邮箱"
      />
      <button type="submit">提交</button>
    </form>
  );
}

非受控组件

表单元素的值由 DOM 自身管理,通过 ref 获取值。

import { useRef } from 'react';

function UncontrolledForm() {
  const nameRef = useRef(null);
  const emailRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    // 通过 ref 获取 DOM 元素的值
    console.log({
      name: nameRef.current.value,
      email: emailRef.current.value,
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        ref={nameRef}
        defaultValue=""
        placeholder="姓名"
      />
      <input
        type="email"
        ref={emailRef}
        defaultValue=""
        placeholder="邮箱"
      />
      <button type="submit">提交</button>
    </form>
  );
}

对比

特性受控组件非受控组件
数据存储React stateDOM
获取值直接读取 state通过 ref
初始值value + statedefaultValue
实时验证容易实现较难实现
代码量较多较少

使用场景

受控组件适用于:

  • 需要实时验证输入
  • 需要根据输入动态控制 UI
  • 需要格式化输入(如手机号、银行卡)
  • 多个输入之间有联动关系
// 实时验证示例
function ValidatedInput() {
  const [value, setValue] = useState('');
  const isValid = value.length >= 3;

  return (
    <div>
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      {!isValid && <span style={{ color: 'red' }}>至少 3 个字符</span>}
    </div>
  );
}

非受控组件适用于:

  • 简单表单,只需提交时获取值
  • 集成第三方 DOM 库
  • 文件上传(<input type="file"> 只能是非受控)
// 文件上传只能用非受控
function FileUpload() {
  const fileRef = useRef(null);

  const handleUpload = () => {
    const file = fileRef.current.files[0];
    console.log(file);
  };

  return <input type="file" ref={fileRef} onChange={handleUpload} />;
}

关键点

  • 受控组件:值存在 state,用 value + onChange
  • 非受控组件:值存在 DOM,用 ref + defaultValue
  • 需要实时响应输入变化时用受控组件
  • 简单表单或文件上传用非受控组件
  • React 官方推荐大多数场景使用受控组件