React 中 setState 什么时候是同步的,什么时候是异步的?
React中正常 state 更新、UI 交互,都离不开用户的事件,比如点击事件,表单输入等,React 是采用事件合成的形式,每一个事件都是由 React 事件系统统一调度的,那么 State 批量更新正是和事件系统息息相关的。 以下为一个点击事件的例子:
export default class index extends React.Component{
state = { number:0 }
handleClick= () => {
this.setState({ number:this.state.number + 1 },()=>{ console.log( 'callback1', this.state.number) })
console.log(this.state.number)
this.setState({ number:this.state.number + 1 },()=>{ console.log( 'callback2', this.state.number) })
console.log(this.state.number)
this.setState({ number:this.state.number + 1 },()=>{ console.log( 'callback3', this.state.number) })
console.log(this.state.number)
}
render(){
return <div>
{ this.state.number }
<button onClick={ this.handleClick } >number++</button>
</div>
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
输出结果为:0 0 0 callback1 1 callback2 1 callback3 1
其中的执行过程如下:
通过开启批量更新开关来处理点击事件,在批量更新条件下合并state结果。
但是异步操作会破坏React的批量更新规则。比如下面的例子:
setTimeout(()=>{
this.setState({ number:this.state.number + 1 },()=>{ console.log( 'callback1', this.state.number) })
console.log(this.state.number)
this.setState({ number:this.state.number + 1 },()=>{ console.log( 'callback2', this.state.number) })
console.log(this.state.number)
this.setState({ number:this.state.number + 1 },()=>{ console.log( 'callback3', this.state.number) })
console.log(this.state.number)
})
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
输出结果为:callback1 1 1 callback2 2 2 callback3 3 3 此时事件系统的执行过程变为:
所以批量更新规则被打破。
那么,如何在如上异步环境下,继续开启批量更新模式呢?
React-Dom 中提供了批量更新方法 unstable_batchedUpdates,可以去手动批量更新,可以将上述 setTimeout 里面的内容做如下修改:
import ReactDOM from 'react-dom'
const { unstable_batchedUpdates } = ReactDOM
1
2
2
setTimeout(()=>{
unstable_batchedUpdates(()=>{
this.setState({ number:this.state.number + 1 })
console.log(this.state.number)
this.setState({ number:this.state.number + 1})
console.log(this.state.number)
this.setState({ number:this.state.number + 1 })
console.log(this.state.number)
})
})
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
输出结果: 0 , 0 , 0 , callback1 1 , callback2 1 ,callback3 1
React-dom 提供了 flushSync ,flushSync 可以将回调函数中的更新任务,放在一个较高的优先级中。React 设定了很多不同优先级的更新任务。如果一次更新任务在 flushSync 回调函数内部,那么将获得一个较高优先级的更新。
handerClick=()=>{
setTimeout(()=>{
this.setState({ number: 1 })
})
this.setState({ number: 2 })
ReactDOM.flushSync(()=>{
this.setState({ number: 3 })
})
this.setState({ number: 4 })
}
render(){
console.log(this.state.number)
return ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
在这个例子中通过ReactDOM的flushSync方法提高了回调函数函数的优先级,所以最先执行number = 3。然后进行批量更新,合并了number = 2 和number = 4,结果输出4。最后执行异步操作,返回1。
编辑 (opens new window)
上次更新: 2021/08/17, 00:26:05