setState函数是异步的还是同步的?
可能很多同学在看到这个问题的时候,甚至搞不清楚这个问题在问什么。
不要慌,我们看一下下面这个例子,首先我们创建一个类组件,这个类组件中,我们定义了state
是一个对象,对象中有一个属性为count
,count
的初始值为0,再页面中,设置了一个h2
标签,标签显示count
的值,同时添加了一个button
按钮,每次点击按钮,使用setState
对count
进行+1,同时输出当前count
的值:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
}
render() {
return (
<div>
<h2>count: {this.state.count}.</h2>
<button onClick={()=>{
this.setState({count:this.state.count+1});
console.log("count",this.state.count);
}}></button>
</div>
);
}
}
运行之后,大家可以尝试一下,点击按钮之后,count在页面中会显示count:1
,但是控制台console.log
输出的是0
;再点击一次,count在页面中会显示count:2
,但是控制台console.log
输出的是1
;不管我们点击多少次,情况一直是这样。
但是从代码的角度上来说,点击之后setState起作用,这个时候console出来的state应该是最新的state才对,但是输出结果却不是这样的,console.log中没有同步更新数据,所以,我们认为:setState是异步处理数据变化的
这句话怎么理解呢?
首先我们来看一下setState的定义,setState接受一个带有形式参数的 updater 函数(也可能直接是一个对象)与一个回调callback(可选)。
setState(updater, [callback])
官方明确表示,setState
对于this.state
并不是立刻更新,若在调用setState
后想要立刻获取到最新的this.state
,那么建议在setState
的callback
或者声明周期componentDidUpdate
中获取
所以,如果立即想获得setState
更新后的数据,可以写在callback
中:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
}
render() {
return (
<div>
<h2>count: {this.state.count}.</h2>
<button onClick={()=>{
this.setState({count:this.state.count+1},()=>{
console.log("count",this.state.count);
});
}}></button>
</div>
);
}
}
这个时候,我们运行后就能发现,我们能立马获得setState
更新后的数据。
回到我们刚开始的问题,setState
是同步的还是异步的,用一句话概括就是:
异步更新,同步执行,setState
函数本身并不是异步的,但是对state
的处理机制给人一种异步的假象,state
处理一般发生在生命周期发生变化的时候;
为什么官方要这样去设置呢,我们继续深挖一下,依然是上面的代码,但是click
函数中setState
函数调用了两次:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
}
render() {
return (
<div>
<h2>count: {this.state.count}.</h2>
<button onClick={()=>{
this.setState({count:this.state.count+1},()=>{
console.log("count",this.state.count);
});
this.setState({count:this.state.count+1},()=>{
console.log("count",this.state.count);
});
}}></button>
</div>
);
}
}
当点击按钮,我们需要连着两次执行setState
,那么react
会帮我们修改两次this.state
然后重新render
两次吗?很明显并不是,react
会批量合并多次setState
操作,上述例子num
最终是2
,且render
在点击后只会渲染一次。
也就是我们所说的,只有生命周期发生变化,state
才会变化,否则this.state
一直拿到的就是上一次渲染后的。
React
在开始重新渲染之前, 会有意地进行"等待",直到所有在组件的事件处理函数内调用的 setState()都完成之后再做最终的this.state
变更,这样可以通过避免不必要的重新渲染来提升性能。
那么,如果我们的目的,就是想让他连着更新两次呢?该怎么修改这段代码呢?
前面我们讲了setState的定义setState(updater, [callback])
,第一个参数updater
除了可以是对象,还可以是函数,写成函数就可以解决这个问题了:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
}
render() {
return (
<div>
<h2>count: {this.state.count}.</h2>
<button onClick={()=>{
//这里函数里的参数的prestate和preprops都是最新更改的上一次的state和props
this.setState((prestate,preprops)=>{return {count:pre.count+1}},()=>{
console.log("count",this.state.count);
});
this.setState((prestate,preprops)=>{return {count:pre.count+1}},()=>{
console.log("count",this.state.count);
});
}}></button>
</div>
);
}
}
这样我们发现,当我们点击按钮,页面的count和终端的count都每次增加2了