react 受控表单必须初始化

react 受控表单必须初始化

场景

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

1
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.

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

1
2
3
4
5
6
7
8
9
/**
* 用户类
*/
export class User {
constructor({ username, password } = {}) {
this.username = username
this.password = password
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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

1
2
3
4
5
6
7
8
9
/**
* 用户类
*/
export class User {
constructor({ username = '', password = '' } = {}) {
this.username = username
this.password = password
}
}

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