问题的诞生
React的setState
方法是个异步方法.所以,若是在setState
之后立即访问state
,往往是不能得到更新之后的state
值的.
试看如下代码:
class Orz extends Component{
constructor(props){
super(props);
this.state = {
value:1,
}
}
componentDidMount(){
this.setState({value:this.state.value+1});
console.log(this.state.value);
this.setState({value:this.state.value+1});
console.log(this.state.value);
this.setState({value:this.state.value+1});
console.log(this.state.value);
this.setState({value:this.state.value+1});
console.log(this.state.value);
}
render(){
return (
<div></div>
);
}
}
挂载这个标签之后,console
里输出:
1
1
1
1
,而并不是我们想象中的
2
3
4
5
.
为了解决这个问题,有三个办法.
笔者推荐第一种方法.若是您觉得第一种方法太naïve,可以采用使用了Promise
的第三种方法.
- 利用
setTimeout
.
代码如下:
componentDidMount(){
setTimeout(() => {
this.setState({value:this.state.value+1});
console.log(this.state.value);
this.setState({value:this.state.value+1});
console.log(this.state.value);
this.setState({value:this.state.value+1});
console.log(this.state.value);
this.setState({value:this.state.value+1});
console.log(this.state.value);
},0)
}
就是把整片代码全部包在setTimeout里面.这样react就会自动强制更新.
- 利用
setState
的回调函数
参考官方文档.
setState(updater, [callback])
其中,updator
是一个函数,如下:
(prevState, props) => stateChange
每当state
得到更新,就会调用callback
函数 .
代码可以是这样:
this.setState(({value}=>{
value:value+1
}),()=>{
console.log(this.state.value);
});
3.利用Promise
,进一步封装方法2
代码由el老师给出.
setStatePromise(updator) {
return new Promise( function (resolve,reject){
this.setState(updator,resolve);
}.bind(this))
}
componentDidMount(){
this.setStatePromise(({value}) => ({
value:value+1
})).then(() => {
console.log(this.state.value);
});
}
这种方法,多连几个then
也就成了地狱.
el老师说:区别在于走楼梯下地狱还是坐电梯下地狱.
第2种和第3种方法,其实并不能从根本上解决问题.
callback
hell还是Promise
hell或者是最简单的setTimeout
, 或者是干脆放弃React呢?请君自行选择.
三种方法背后的原理
"深入理解React技术栈"这本书中关于setState的机制有详细描述.
笔者简录如下:
setState
是一个异步方法.由队列实现.它有Batch模式(批量更新模式),和普通模式.
普通模式下,
setState
能够即时更新state
.Batch模式下,
setState
会将队列中的state
进行合并,然后就会出各种状况.
setTimeout
就是一个强制使用普通模式的方法.
也可以去看看这本书作者(陈屹)管理的知乎专栏pure render. 问题引入代码改编自这本书上的例子.
(标题只是为了搞笑)