Skip to content

非受控组件

非受控组件(Uncontrolled Components)是 React 中处理表单输入的另一种方式。与受控组件不同,非受控组件中的表单元素的值不由 React 状态管理,而是直接保存在 DOM 中。你可以使用 ref 来获取这些元素的当前值。这种方式更接近传统的 HTML 表单处理方式,在某些情况下可能会更加简洁。

非受控组件的基本概念

  • 状态(State):不通过 React 的状态来保存表单字段的值。
  • 引用(Ref):使用 useRefReact.createRef 创建引用对象,并将其赋值给表单元素的 ref 属性,以便可以直接访问 DOM 节点。
  • 事件处理器(Event Handlers):通常不需要为每个输入单独设置 onChange 事件处理器,因为表单数据直接存储在 DOM 中。
  • 同步(Synchronization):表单元素的值直接由用户输入更新,无需额外的同步逻辑。

示例:文本输入框

以下是一个简单的例子,展示了如何创建一个非受控的文本输入框:

jsx
import React, { useRef } from "react";

function UncontrolledForm() {
  const inputEl = useRef(null);

  function handleSubmit(event) {
    alert("A name was submitted: " + inputEl.current.value);
    event.preventDefault();
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>
          Name:
          <input ref={inputEl} type="text" />
        </label>
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

export default UncontrolledForm;

在这个例子中,我们使用 useRef 创建了一个引用对象 inputEl,并将其赋值给 <input> 元素的 ref 属性。然后在提交表单时,可以通过 inputEl.current.value 获取输入框的值。

处理多个输入

对于包含多个输入字段的表单,你可以为每个需要访问的输入元素创建独立的 ref,或者使用其他方法来收集所有输入的数据。

示例:使用多个 ref

jsx
import React, { useRef } from "react";

function FormWithMultipleInputs() {
  const usernameInput = useRef(null);
  const emailInput = useRef(null);

  function handleSubmit(event) {
    alert(
      `Username: ${usernameInput.current.value}, Email: ${emailInput.current.value}`
    );
    event.preventDefault();
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>
          Username:
          <input ref={usernameInput} type="text" />
        </label>
      </div>
      <div>
        <label>
          Email:
          <input ref={emailInput} type="email" />
        </label>
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

export default FormWithMultipleInputs;

非受控选择框(Select Box)

处理 <select> 元素与处理文本输入类似,但需要注意的是你需要为每个选项设置 value 属性,并且在提交时从 ref 中读取所选选项的值。

jsx
import React, { useRef } from "react";

function FlavorForm() {
  const selectEl = useRef(null);

  function handleSubmit(event) {
    alert("Your favorite flavor is: " + selectEl.current.value);
    event.preventDefault();
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Pick your favorite La Croix flavor:
        <select ref={selectEl}>
          <option value="grapefruit">Grapefruit</option>
          <option value="lime">Lime</option>
          <option value="coconut">Coconut</option>
          <option value="mango">Mango</option>
        </select>
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
}

export default FlavorForm;

非受控复选框(Checkbox)

处理复选框稍微有些不同,因为它们有 checked 而不是 value 属性。你需要用布尔值来表示是否选中,并在提交时检查 ref.current.checked

jsx
import React, { useRef } from "react";

function ToggleButton() {
  const checkboxRef = useRef(null);

  function handleSubmit(event) {
    alert(
      "Checkbox is " + (checkboxRef.current.checked ? "checked" : "not checked")
    );
    event.preventDefault();
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Toggle me
        <input type="checkbox" ref={checkboxRef} />
      </label>
      <input type="submit" value="Check Status" />
    </form>
  );
}

export default ToggleButton;

文件上传

处理文件上传时,你可以使用 <input type="file" /> 元素,并结合非受控的方式读取文件信息。

示例:非受控文件上传

jsx
import React, { useRef } from "react";

function FileUpload() {
  const fileInput = useRef(null);

  function handleSubmit(event) {
    if (fileInput.current.files.length > 0) {
      console.log(fileInput.current.files[0]);
    }
    event.preventDefault();
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>
          Upload file:
          <input type="file" ref={fileInput} />
        </label>
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

export default FileUpload;

表单验证

React 不提供内置的表单验证机制,但你可以在提交表单时手动进行验证。对于非受控组件,你需要从 ref 中获取表单数据,并根据需要显示错误消息。

示例:简单验证

jsx
import React, { useRef } from "react";

function ValidatedForm() {
  const emailInput = useRef(null);
  const errorRef = useRef(null);

  function validateEmail(email) {
    // 简单的正则表达式验证
    const re = /\S+@\S+\.\S+/;
    return re.test(String(email).toLowerCase());
  }

  function handleSubmit(event) {
    const email = emailInput.current.value;
    if (!validateEmail(email)) {
      errorRef.current.textContent = "Invalid email address";
    } else {
      errorRef.current.textContent = "";
      alert("Valid email address!");
    }
    event.preventDefault();
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>
          Email:
          <input type="email" ref={emailInput} />
        </label>
      </div>
      <p style={{ color: "red", marginBottom: "10px" }} ref={errorRef}></p>
      <button type="submit">Submit</button>
    </form>
  );
}

export default ValidatedForm;

在这个例子中,我们在提交表单时调用 validateEmail 函数来检查电子邮件地址是否有效。如果无效,则设置错误消息;否则,显示成功提示。

使用默认值

你可以使用 defaultValue 属性为非受控组件设置初始值,这类似于 HTML 中的 value 属性,但它不会随着用户的输入而改变。

jsx
import React, { useRef } from "react";

function DefaultValuedForm() {
  const inputEl = useRef(null);

  function handleSubmit(event) {
    alert("A name was submitted: " + inputEl.current.value);
    event.preventDefault();
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>
          Name:
          <input ref={inputEl} type="text" defaultValue="Initial Value" />
        </label>
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

export default DefaultValuedForm;

总结

非受控组件提供了比受控组件更简洁的方式来处理表单,特别是在你只需要偶尔访问表单数据或不想将所有表单状态都保存在 React 中的情况下。然而,它们也有一些局限性,比如缺乏自动化的状态管理和更复杂的交互逻辑。因此,选择哪种方式取决于你的具体需求和个人偏好。

Released under the MIT License.