react-controlled-uncontrolled

test12026-10-2512 次阅读

表单的两种心智模型

React 里表单输入有两种写法。受控组件把值放进 state,每次按键都触发一次重渲染;非受控组件把值留在 DOM 上,只在真正需要时通过 ref 读出来。两者没有优劣,只有适用场景。

受控组件:值始终在 React 手里

受控组件的好处是数据单一来源、随时可得,适合做实时校验、根据输入动态禁用按钮。代价是每次按键都有一次渲染。

import { useState, FormEvent } from "react";

function ControlledInput() {
  const [value, setValue] = useState("");
  const tooShort = value.trim().length < 3;
  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}
      style={{ borderColor: tooShort ? "red" : "#ccc" }}
    />
  );
}

非受控组件:值留在 DOM 上

非受控组件用 ref 直接读 DOM,提交时一次性取值。它适合大表单、文件上传、需要接入非 React 库,或者单纯想避免每次按键都重渲染的场景。

import { useRef, FormEvent } from "react";

function UncontrolledForm() {
  const nameRef = useRef<HTMLInputElement>(null);

  function onSubmit(e: FormEvent) {
    e.preventDefault();
    const name = nameRef.current?.value ?? "";
    console.log("提交:", name);
  }

  return (
    <form onSubmit={onSubmit}>
      <input ref={nameRef} defaultValue="" />
      <button type="submit">提交</button>
    </form>
  );
}

文件输入只能非受控

文件输入框没有 value 属性,天然是非受控的。通过 ref 读取 FileList 即可。

function AvatarUpload() {
  const fileRef = useRef<HTMLInputElement>(null);
  function onSubmit(e: FormEvent) {
    e.preventDefault();
    const file = fileRef.current?.files?.[0];
    if (file) upload(file);
  }
  return (
    <form onSubmit={onSubmit}>
      <input ref={fileRef} type="file" accept="image/*" />
    </form>
  );
}

需要重置时,换 key 而不是清空 ref

想清空一组非受控输入,与其一个个重置,不如给外层元素换一个 key 触发整棵子树重新挂载,这才是 React 习惯的做法。

该怎么选

  • 实时校验、联动禁用、输入即搜索:用受控。
  • 文件上传、几十上百个字段的大表单、性能敏感:用非受控。
  • React 19 起非受控也能把 ref 作为 prop 传给子组件,并可返回一个清理函数,配合 useImperativeHandle 还能暴露 reset 方法。

一句话:值要不要被 React 实时参与计算,决定了它该不该进 state。

T

test1

文章作者

表单的两种心智模型 React 里表单输入有两种写法。受控组件把值放进 state,每次按键都触发一次重渲染;非受控组...

分类
技术
发布时间
2026-10-25
字数
约 1726 字
阅读
12 次