谈到React,不得不提到的就是state,相信你一定用过很多次的setState,也知道setState是一个异步方法,正如官网说的:

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

setState调用位置

从React调用stateState的位置来看情况无非有2种:

  • 一种是在生命周期函数中 componentWillMountcomponentDidMountcomponentWillReceivePropsrender
  • 在各种handler中(包括组件本身元素的handler和通过this.props.handerXXX)

在handler中setState

思考以下代码运行结果

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
class App extends React.Component {
constructor(props){
super(props);
this.state = {
count: 0
};
this.clickHandler = this.clickHandler.bind(this);
}
clickHandler(){
this.setState({
count: this.state.count + 1
})
console.log("log 1 :", this.state.count); // log 1
this.setState({
count: this.state.count + 1
})
console.log("log 2 :", this.state.count); // log 2
}
render(){
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.clickHandler}>Count Add</button>
</div>
);
}
}

试着想一下当点击button的时候2次log的结果分别是什么?先不急着公布答案,在来看看下面的代码

在生命周期中setState

再次思考以下代码运行结果

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
class App extends React.Component {
constructor(props){
super(props);
this.state = {
count: 0
};
}
componentWillMount(){
this.setState({
count: this.state.count + 1
})
console.log("log 3 :", this.state.count); // log 3
this.setState({
count: this.state.count + 1
})
console.log("log 4 :", this.state.count); // log 4
}
render(){
return (
<div>
<p>Count: {this.state.count}</p>
</div>
);
}
}

你一定知道在componentWillMount调用setState是会合并state而不会触发re-render,那么log3和log4分别输出多少呢?

setState调用方式

众所周知,在JavaScript中充满了各种异步函数,当然setState也可以被异步调用,再来思考下面的代码:

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
class App extends React.Component {
constructor(props){
super(props);
this.state = {
count: 0
};
}
componentWillMount(){
setTimeout(()=> {
this.setState({
count: this.state.count + 1
})
console.log("log 5 :", this.state.count); // log 5
this.setState({
count: this.state.count + 1
})
console.log("log 6 :", this.state.count); // log 6
}
}, 0)
}
render(){
return (
<div>
<p>Count: {this.state.count}</p>
</div>
);
}
}

上面的setState是被放入异步执行队列被执行的,那么log5和log6分别输出多少呢?(在各种handler回调中执行的异步setState类似这里的5和6)

答案

log1和log2