setState不及时更新的问题

老早前在做项目的时候,遇到表单设置select下拉,选择完选项 在其他function函数里面需要拿这个选择的数据,通过 this.state.XXX 去获取,总是拿到一个旧的数据,不会及时的更新,在render里面去拿就能拿到新的数据,当时也没好好去深入,现在做个笔记希望能够思路清晰点。

React官方的文档解释是: Link
大致的意思是 setState()不会立刻改变 this.state,而是创建一个即将处理的state。setState()并不一定是同步的,为了提升性能React会批量(也就是合并多次的setState)执行state和DOM渲染。
setState()总是会触发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var Counter = React.createClass({
getInitialState: function () {
return { clickCount: 0 };
},
handleClick: function () {
this.setState({
clickCount: this.state.clickCount + 1
});
console.log(this.state.clickCount) // 第一次点击得到 0
},
render: function () {
return (<h2 onClick={this.handleClick}>次数为: {this.state.clickCount}</h2>); // 第一次点击得到 1
}
});
ReactDOM.render(
<Counter />,
document.getElementById('message')
);

再来试试所谓的合并多次state问题,同样的在这个Demo里我handleClick多次setState 看看是什么情况?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var Counter = React.createClass({
getInitialState: function () {
return { clickCount: 0 };
},
handleClick: function () {
this.setState({
clickCount: this.state.clickCount + 1
});
this.setState({
clickCount: this.state.clickCount + 2
});
console.log(this.state.clickCount) // 依次得到 0, 2, 4 ...
},
render: function () {
return (<h2 onClick={this.handleClick}>次数为: {this.state.clickCount}</h2>); // 依次得到 2, 4, 6 ..
}
});
ReactDOM.render(
<Counter />,
document.getElementById('message')
);

也就是说 在handleClick执行多次setState , react只会执行最后一次的结果。 如果我用计时器setTimeout 看看会有啥新的发现?

1
2
3
4
5
6
7
8
9
handleClick: function () {
this.setState(function(state) {
return {clickCount: state.clickCount + 1};
});
setTimeout(()=>{
console.log(this.state.clickCount) // 第一次点击 输出 1 render也同样取到 1
},100)
}

另一个方式则是用componentDidUpdate()这个生命周期方法,把确定state更新后要执行的代码放在里面,如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var Counter = React.createClass({
getInitialState: function () {
return { clickCount: 0 };
},
handleClick: function () {
this.setState(function(state) {
return {clickCount: state.clickCount + 1};
});
},
componentDidUpdate(){
console.log(this.state.clickCount) // 第一次点击 输出 1
},
render: function () {
console.log('render');
return (<h2 onClick={this.handleClick}>次数为: {this.state.clickCount}</h2>); // render也同样取到 1
}
});
ReactDOM.render(
<Counter />,
document.getElementById('message')
);

react 官方的说明方法提供了callback的回调方式获取最新的state状态

setState(object nextState[, function callback])

  1. nextState,将要设置的新状态,该状态会和当前的state合并
  2. callback,可选参数,回调函数。该函数会在setState设置成功,且组件重新渲染后调用。
1
2
3
4
5
handleClick: function () {
this.setState({clickCount: this.state.clickCount + 1}, function() {
console.log(this.state.clickCount) // 依次输出 1, 2, 3 ...
});
}

[参考]
为什么setState没有立即执行
为何说setState方法是异步的?
解密 setState

0%