react 受控表单必须初始化

本文最后更新于:2020年12月30日 下午

场景

这些天在学习 React 的时候遇到了一个奇怪的问题,明明受控表单的双向绑定已经成功了,然而控制台还是会出现 react 的警告:

Warning: A component is changing an uncontrolled input of type undefined to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

代码很简单,仅仅只是一个登录表单

/**
 * 用户类
 */
export class User {
  constructor({ username, password } = {}) {
    this.username = username;
    this.password = password;
  }
}
import React, { Component } from "react";
import { User } from "./User";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      user: new User(),
    };
  }
  change = (e) => {
    const el = e.target;
    const k = el.name;
    const v = el.value;
    const user = { ...this.state.user };
    user[k] = v;
    this.setState({
      user,
    });
  };
  submit = () => {};
  reset = () => {
    this.setState({
      user: new User(),
    });
  };
  render() {
    const { username, password } = this.state.user;
    return (
      <div>
        <div>
          <label htmlFor="username">用户名: </label>
          <input name="username" value={username} onChange={this.change} />
        </div>
        <div>
          <label htmlFor="password">密码: </label>
          <input name="password" value={password} onChange={this.change} />
        </div>
        <div>
          <button onClick={this.submit}>登录</button>
          <button onClick={this.reset}>重置</button>
        </div>
      </div>
    );
  }
}

export default App;

在 App 组件的 constructor 中明明已经通过 new User() 初始化了 user 属性,然而在输入的时候,还是会出现警告。

注:此时在输入框中输入值,确实会影响到 react state 中的 user 属性,反之亦然。只有一点,当重置表单,即使用 this.setState({user: new User()}) 重置 user 对象无法影响到页面上输入框的值。

此处出现了两个问题

  1. 为什么在输入的时候会出现警告
  2. 为什么重置之后输入框的值没有变化

解决

最终,吾辈在 StackOverflow 上找到了答案。
很重要的一句话:对于要控制的输入,其值必须与状态变量的值相对应。
最初并未满足这个条件,值为 nullstate 属性会被 react 视为未定义,导致表单最初是不受控制的。但是,当 onChange 第一次被触发的时候,this.state.user.username 就被设置了。此时,满足了条件,从非受控表单转换为了受控表单并导致了控制台的警告。
同理,当使用 this.setState({user: new User()}) 重置的时候,又变成了非受控表单,所以这里的绑定再次失效了。

注: react 使用 == 而非 === 比较是否为 null,而 null == undefined 的值为 true,所以。。。

那么,知道问题了之后,我们只要保证初始值 val != null 即可。
例如上面的代码可以修改 User.js

/**
 * 用户类
 */
export class User {
  constructor({ username = "", password = "" } = {}) {
    this.username = username;
    this.password = password;
  }
}

那么,关于 react 中的受控表单初始化的问题便到此为止了。可想而知,react 的坑还有很多没有踩完呢


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!