setState到底是同步还是异步?
其实setState可能表现为异步更新也可能表现为同步更新
异步更新情况
生命周期中
state = {
number:1
};
componentDidMount(){
this.setState({number:3})
console.log(this.state.number) //输出的是更新前的number --> 1
}
合成事件中
首先得了解一下什么是合成事件,react为了解决跨平台,兼容性问题,自己封装了一套事件机制,代理了原生的事件,像在jsx
中常见的onClick
、onChange
这些都是合成事件。
class App extends Component {
state = { number: 0 }
increment = () => {
this.setState({ number: this.state.number + 1 })
console.log(this.state.number) // 输出的是更新前的number --> 0
}
render() {
return (
<div onClick={this.increment}>
{`Counter is: ${this.state.number}`}
</div>
)
}
}
那如果我们想拿到更新之后的值应该怎么办呢?主要有以下两种方法
获取setState实时更新过后的值的方法
回调函数
setState提供了一个回调函数供开发者使用,在回调函数中,我们可以实时的获取到更新之后的数据
state = {
number:1
};
componentDidMount(){
this.setState({number:3},()=>{
console.log(this.state.number) // 3
})
}
这个时候大家可以看到控制台打印的数据就是最新的了,我们也就实时的获取到了最新的数据。
componentDidUpdate 生命周期
class App extends Component {
constructor(props) {
super(props);
this.state = {
number: 0
}
}
render() {
return (
<div>
<h2>当前计数: {this.state.number}</h2>
<button onClick={e => this.changeText()}>改变数字</button>
</div>
)
}
componentDidUpdate() {
// 方式二: 获取异步更新的state
console.log(this.state.number);
}
changeText() {
this.setState({
number:this.state.number + 1
})
}
}
为什么在合成事件和生命周期中会表现为异步更新呢?
归根结底还是因为react框架本身的性能机制所导致的。因为每次调用setState都会触发更新,异步操作是为了提高性能,将多个状态合并一起更新,减少render调用。
试想一下如果在组件中有以下这样一段代码执行:
for ( let i = 0; i < 100; i++ ) {
this.setState( { num: this.state.num + 1 } );
}
如果setState是一个同步执行的机制,那么这个组件会被重新渲染100次,这对性能是一个相当大的消耗。 显然,React也是想到了这个问题,因此对setState做了一些特殊的优化:
React会将多个setState的调用合并为一个来执行,也就是说,当执行setState的时候,state中的数据并不会马上更新,所以打印的值,就会得到更新之前的值。
同步更新情况
setTimeout
state = {
number:1
};
componentDidMount(){
setTimeout(()=>{
this.setState({number:3})
console.log(this.state.number) // 3
},0)
}
原生事件中
state = {
number:1
};
componentDidMount() {
document.body.addEventListener('click', this.changeVal, false);
}
changeVal = () => {
this.setState({
number: 3
})
console.log(this.state.number) // 3
}
为什么上面这两种情况又表现为同步更新呢?
原生事件可以简单理解为因为没有走合成事件的那一大堆东西,跳过了react的机制,直接触发click事件,所以当你在原生事件中setState后,能同步拿到更新后的state值。
setTimeout 可以简单理解为,跳过了react的性能机制,所以也可以同步拿到更新后的state值。
setState中的批量更新
export default class App extends Component {
state = {
counter: 0
}
render() {
return (
<div>
<h2>当前计数: {this.state.counter}</h2>
<button onClick={e => this.increment()}>+1</button>
</div>
)
}
increment() {
// 1.setState本身被合并 不会进行累加 只会加1
this.setState({
counter: this.state.counter + 1
});
this.setState({
counter: this.state.counter + 1
});
this.setState({
counter: this.state.counter + 1
});
}
}
- 直接传递对象的
setstate
会被合并成一次,只会生效一次,只会加 1.
下面这种写法,就不会被合并,会生效三次。
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
总结
-
setState在合成事件和生命周期函数中是“异步”的,在原生事件和 setTimeout中是同步的
-
可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。或者在componentDidUpdate生命周期里面拿到更新后的结果
-
多次setState更新会合并为一次,如果有相同的更新会被覆盖,只会执行一次
限制 state 更新视图
对于类组件如何限制 state 带来的更新作用的呢?
- pureComponent 可以对 state 和 props 进行浅比较,如果没有发生变化,那么组件不更新。
- shouldComponentUpdate 生命周期可以通过判断前后 state 变化来决定组件需不需要更新,需要更新返回true,否则返回false。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!