受控组件与非受控组件

React 中的受控组件和非受控组件都是指的表单组件,其区别主要是表单值是由 React 处理还是由浏览器处理。

受控组件

表单元素,如inputtextareaselect等,通常都是自行维护 State 并根据用户的输入进行更新。通过与 React 组件中的 State 进行组合,可以使 React 组件中的 State 成为唯一数据源。渲染表单的 React 组件在控制组件 State 的同时,还控制用户输入过程中的表单操作,还可以更方便的对用户输入的内容进行验证,这就形成了受控组件。

所以在项目中,一般可以通过以下方式来获取用户的输入。

function UserForm(props) {
  const [formState, setFormState] = useState({
    username: "",
    password: "",
  });
  const handleChange = (event) => {
    event.preventDefault();
    setFormState({ ...formState, [event.target.name]: event.target.value });
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        用户名:
        <input
          type="text"
          name="username"
          value={formState.username}
          onChange={handleChange}
        />
      </label>
      <label>
        密码:
        <input
          type="password"
          name="password"
          value={formState.password}
          onChange={handleChange}
        />
      </label>
    </form>
  );
}

Caution

在使用受控组件的时候,因为组建的State已经成为了表单组件展示数据的数据源,如果不手动响应表单组件的onChange等方法,表单组件中展示的内容是不会发生改变的。

非受控组件

受控组件的 State 数据都保存在 React 组件的 State 中,这就需要在 React 组件中编写大量用于控制 DOM 节点的逻辑。虽然在大部分情况下推荐使用受控组件来处理表单数据,但是在表单规模很大或者使用文件上传时,可以使用非受控组件来替代处理。

非受控组件可以使用ref来从 DOM 节点中获取表单数据。以下是上例的替代示例。

function UserForm() {
  const user = useRef();
  const pass = useRef();

  const handleSubmit = (event) => {
    const formData = {
      user: user.current.value,
      pass: pass.current.value,
    };
    event.preventDefault();
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        用户名:
        <input type="text" ref={user} />
      </label>
      <label>
        密码:
        <input type="password" ref={pass} />
      </label>
    </form>
  );
}

在使用ref来获取非受控组件的值时,通常需要给定组件一个初始值,这个初始值可以在组件上通过属性defaultValuedefaultChecked来赋予。