在React18版本中,setState无论是在原生事件、setTimeout、promise等方法中都是异步处理,并进行批量更新,但在18版本之前setState是同步处理。
1.setState为什么被设置为异步处理
- 设置为异步可以显著提升性能,如果每次调用setState都进行一次更新,那么意味着render函数会被频繁的调用界面重新渲染。React18将获取到多个更新,之后进行批量更新。
- 如果同步更新了state,但还没有执行render函数,那么state和props不能保持同步,这对开发是很大问题
2.setState中的批量更新
export class App extends Component {
constructor(){
super()
this.state = {
val:0
}
}
batchUpdates = () => {
const {val} = this.state
this.setState({ val: val + 1 })
console.log(val,'once');
this.setState({ val: val + 1 })
console.log(val,'twice');
this.setState({ val: val + 1 })
console.log(val,'thrice');
}
render() {
const {val} = this.state
console.log('render被执行');
return (
<div>
<h2>{val}</h2>
<button onClick={this.batchUpdates}>自增</button>
</div>
)
}
}
如上代码,batchUpdates函数设置三次setState对val进行加一操作,但实际结果却是每次点击按钮val值只会自加一次,并且render函数执行了一次,同时这也验证了在React18中setState中是异步的,否则会打印出1,2,3
3.state和props保持同步更新
现在使用setState对val值进行修改,并将val值传给子组件,如果setState是同步的,render函数并不会执行,传递给子组件的val值依然还是0
4.setState在原生事件中
class App extends Component {
state = { val: 0 }
changeValue = () => {
this.setState({ val: this.state.val + 1 })
console.log(this.state.val) // 输出的是更新后的值 --> 1
}
componentDidMount() {
document.body.addEventListener('click', this.changeValue, false)
}
render() {
return (
<div>
{`Counter is: ${this.state.val}`}
</div>
)
}
}
5.setState在生命周期中
componentDidMount() {
const {val} = this.state
this.setState({ val: val + 1 })
console.log(val,'first')
this.setState({ val: val + 1 })
console.log(val,'second')
setTimeout(_ => {
this.setState({ val: val + 1 })
console.log(val,'third');
this.setState({ val: val + 1 })
console.log(val,'forth')
}, 0)
}
6.在React18中执行同步
import React, { Component } from "react";
import { flushSync } from "react-dom";
export class App extends Component {
constructor(){
super()
this.state = {
val:0
}
}
componentDidMount() {
flushSync(()=>{
this.setState({val:1})
})
}
render() {
console.log('render');
const {val} = this.state
return (
<div>
<h2>{val}</h2>
</div>
)
}
}
export default App;
7.总结
在React18之前
- 在组件生命周期或React合成事件中,setState是异步
- 在setTimeout、原生dom事件中、promise中,setState是同步
在React18中统一是异步的