跳至主要內容

受控组件与非受控组件

Mr.He大约 2 分钟

受控组件与非受控组件

受控组件

表单数据交由state对象管理, 受控表单往往给予了我们更大的选择,例如比较复杂的、非 HTML 标准的表单校验,如检查密码强度和对用户手机号进行格式化。

特点:可以实时得到表单数据,代码相对复杂

import React, { useState } from 'react'

function ControlledForm() {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = () => {
    sendInputValueToApi(value).then(() => /** 业务逻辑... */);
  };

  return (
    <>
      <input type="text" value={value} onChange={handleChange} />
      <button onClick={handleSubmit}>send</button>
    </>
  )
}

因为数据已经保存在 state 中,所以我们并不需要真正的 onSubmit 事件,而且在按钮点击时,我们也并不需要直接访问 input 的值。

受控缺点

  • 你可能不想要每次用户输入时都去重新渲染组件。

  • 你需要写许多代码去管理复杂的表单,因为随着表单规模的增长,会导致出现大量的 state 和 setSate,从而使代码变的非常臃肿。

  • 构建动态表单将变的非常困难,因为你无法在条件判断中使用像 useState 的 hooks。

  • 整个表单的值将存储在一个巨大的对象中,然而这会导致所有的子组件将在任一其他组件变化时全部重新渲染,因为我们更新的方式是 setState({ ...preState, field: newValue })

  • 在大型表单例如表格和 Excel 中,这会导致性能问题。

非受控组件

非受控表单使用原生 HTML 内置的 <form> 的功能和 JavaScript 去管理数据。

举例来说,浏览器会帮我们管理状态,我们无需在每次 input 改变时使用 setState 更新 state 并把 state 设置到 input 的 value 属性上,我们的组件不再需要或使用这些 state

特点:表单数据在需要时进行获取,代码实现相对简单。

function UncontrolledForm() {

  const handleSubmit = (event) => {
    event.preventDefault();

    const formData = new FormData(event.target);
    const inputValue = formData.get('inputName');

    sendInputValueToApi(inputValue).then(() => /* 业务逻辑... */)
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="inputName" />
      <button type="submit">Send</button>
    </form>
  );
}

混合使用

将受控 input 抽离出来以此来减少重新渲染对表单其余部分的影响。

const PhoneInput = () => {
  const [phoneNumber, setPhoneNumber] = useState("");

  const handlePhoneNumberChange = (event) => {
    const formattedNumber = formatPhoneNumber(event.target.value);
    setPhoneNumber(formattedNumber);
  };

  return (
    <input
      type="tel"
      name="phoneNumber"
      value={phoneNumber}
      onChange={handlePhoneNumberChange}
    />
  );
};

function MixedForm() {
  const handleSubmit = (event) => {
    event.preventDefault();
    const formData = new FormData(event.target);
    for (let [key, value] of formData.entries()) {
      console.log(`${key}: ${value}`);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>Name:</label>
      <input type="text" name="name" />

      <label>Email:</label>
      <input type="email" name="email" />

      <label>Phone Number:</label>
      <PhoneInput />

      <label>Address:</label>
      <input type="text" name="address" />

      <button type="submit">Submit</button>
    </form>
  );
}

function formatPhoneNumber(number) {
  return number.replace(/\D/g, "").slice(0, 10);
}

选用标准

大多数情况下,推荐受控组件处理表单,除非表单交互比较简单