这个是写某项目过程当中偶然遇到的,具体需求是随着数据的变化,实时改变一个 <input>
输入框的 value。因为用 React.js,自然很快就想到利用 React 组件的 state 特性就可以很方便的做到了。然而由此却引发了一些问题,就是我发现用 this.state.val
赋值,用 this.setState()
改变组件状态的时候,更新并没有实时展现出来(其实是根本没有展示出来),输入框里的内容并没有实时更新。
我们知道通过调用 React 组件的 setState()
方法改变组件的状态会触发组件重新渲染,则用 this.state
调用的 state 也会改变。所以说,输入框的值没有实时更新,很可能是因为组件根本没有重新渲染。一开始以为是回调的问题,于是使用 setState()
的第二个参数处理接下来的业务逻辑,可是发现并不是。最后发现,问题出现在对 input 的 value 属性的设置上。
考虑下面的这一段代码:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
val: ''
};
setTimeout(() => {
this.setState({
val: 'world!'
});
}, 1000);
}
render() {
return (
<div>
<span>Hello</span>
<input type='text' defaultValue={this.state.val} />
</div>
);
}
}
如果正常,将 <App />
渲染到页面上,你可以看到 Hello 。然后稍等 1s,你可以看到 Hello 。
这就是你说的正常显示?我读书少你别骗我!”
的确,如果单单读这段代码,一般人都应该能猜出想象中的结果——文本框最开始为空,但是一秒后它的值会变为 world。可是这里却并没有实现。文档并没有写错,this.setState()
过后会引起状态的变更,虽然是异步的但是迟早会做这一步的啊。为什么在这里无效呢。
问题的关键在于,我们为输入框赋值的时候用的属性是 defaultValue
。这是一个只读属性。
“切,这就是你的锅了,好好的 value 属性不用,用个 poi 的 defaultValue!”
可是假如你把 defaultValue 改成 value,那么请你打开 console,那里已经有一条红色的警告消息等着你了。不信的话大家不妨试试:
React 要求我们设置这个 input 为只读,或者为这个 input 设置一个 onChange 事件,如果都不的话就用 defaultValue 代替 value。很多人为了省事就听了它的话用了 defaultValue(包括我),于是就踩进了坑。下面我们试着改一下上面那个组件的 render 方法好不好啊?吼啊!(((
javascript return (
把 defaultValue 改成 value 并且绑定一个 onChange 事件。再次刷新页面你会发现出现了我们预期中的结果。至此我们可以大声讲出这个问题就是有这个只读的属性 defaultValue 引起的了。