React 踩坑--input中的value与defaultValue
这两个月实习期时间踩了不少坑,先来谈谈有代表性的一个。
我们知道表单是前端里很常见的一个东西,往往包含了很多数据校验逻辑。
React、antd 对表单元素专门做了优化处理,有了一些抽象的东西,使得他们的使用方式更统一更规范。。
在一次需求开发中,有一块是这样的:首先渲染一个Table,里面每一项都有个“编辑”操作,点击弹出Modal框
如图所示:
这个Modal框是一个表单,里面的文本框 下拉框等 都带有从Table里对应的那一项传来的默认值
于是我习惯性地想到了设置 “defaultValue”,如下图。。
但跑起来后发现这样一个问题:
第一次点开弹出框,他带的默认值是正确的:
但当我在Table里改变了此项数据再次点开,它并没有按照设想的显示改变后的选项,而是一直固定在“是”。
解决这个问题前,先来回顾一下React的 受控和非受控组件
“在 HTML 中,表单元素(如<input>
、 <textarea>
和 <select>
)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()
来更新。我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。” ——React docs
这里有一个新的概念叫“受控组件”。那么如何理解受控组件和非受控组件呢。
受控组件,简单的说,就是由React管理了它的value,而非受控组件的value就是原生的DOM管理的。
他们的写法上也有很大区别。
例如,非受控组件这么写:
<input type="text" defaultValue="a" />
这个 defaultValue 其实就是原生DOM中的 value 属性。
这样写出的来的组件,其value值就是用户输入的内容,React完全不管、也管不到输入的过程。
而受控组件是这么写的:
<input type="text" value={this.state.name} onChange={this.handleChange} />
handleChange: function(e) {
this.setState({name: e.target.value});
}
这里,value属性不再是一个写死的值,他是 this.state.name,而 this.state.name 是由 this.handleChange 负责管理的。
这个时候实际上 input 的 value 根本不是用户输入的内容。而是onChange 事件触发之后,由于 this.setState 导致了一次重新渲染。
同时,React会优化这个渲染过程,实际上它仍然是通过设置input的value来实现的。
但一定要注意,受控组件显示的值和用户输入的值虽然很多时候是相同的,但他们根本是两码事。
受控组件显示的是 this.state.name 的值。你可以在handleChange中对用户输入的值做任意的处理,比如数据校验。
对比受控组件和非受控组件的输入流程:
- 非受控组件: 用户输入A => input 中显示A
- 受控组件: 用户输入A => 触发onChange事件 => handleChange 中设置 state.name = “A” => 渲染input使他的value变成A
正式因为这样,使得 React 的 state 成为唯一数据源。对于受控组件来说,输入的值始终由 React 的 state 驱动。
所以官方强烈推荐使用受控组件,因为它能更好的控制组件的生命流程。
所以回到我代码中的问题,解决办法就是:
将defaultValue 改成 value={this.state.XXX},然后在外边写一个 handleXXX 函数,通过 setState 来控制value的值。
使之成为一个受控组件,这样就没问题了