学习react你一定使用了它的setState,那么它的更新策略是什么那?内部是如何运行的那?下面内容给你解释清楚。 react开发对setState的使用可能一点也不陌生,但肯定会碰到过这种情况
import React from 'react'
export default class BatchedDemo extends React.Component {
state = {
number: 0,
}
handleClick = () => {
this.countNumber()
}
countNumber() {
this.setState({
number: this.state.number + 1
})
this.setState({
number: this.state.number + 4
})
this.setState({
number: this.state.number + 10
})
}
render() {
return <button id="myButton" onClick={this.handleClick}>Num: {this.state.number}</button>
}
}
运行结果为 10 这是为什么那? 这就是我们下面需要说到的 更新策略了
setState批量更新
- 除了 虚拟DOM 外,减少更新频率的另一个手段就是React的批量更新。
- 顾名思义,批量处理就是,可以避免短期内的多次渲染,攒为一次更新。
setState()合并策略
经过测试发现:一次更新的意思就是,覆盖原来的,而不是叠加原来的。 正如上面的那个结果一样:结果为10 而不是 15。
那
s
e
t
S
t
a
t
e
是
如
何
合
并
的
那
?
是
否
可
以
控
制
合
并
那
?
\color{red}{那setState是如何合并的那?是否可以控制合并那?}
那setState是如何合并的那?是否可以控制合并那?
setState的合并原理:
setState实现:
setState(newState) {
if (this.canMerge) {
this.updateQueue.push(newState)
return
}
...
}
然后countNumber()方法调用之后,把隐式操作通过伪代码显示出来:
countNumber() {
this.canMerge = true
this.setState({
number: this.state.number + 11
})
this.setState({
number: this.state.number + 20
})
this.setState({
number: this.state.number + 5,
})
this.canMerge = false
const finalState = ...
this.setState(finaleState)
}
可以看出 setState首先会判断是否可以合并,如果可以合并this.canMerge = true ,就直接返回了。直到this.canMerge = false时,代表finalState已经合并完成,就开始走更新,需要注意的是这些都是react内部的隐式操作,是发生在React内部的,React对它们有完全的控制权。
canMerge逻辑存在于哪里?
除了事件处理函数会执行canMerge逻辑,在执行componentDidMount前后也会有canMerge逻辑,可以理解为:React委托代理了所有的事件,在执行你的函数/componentDidMount之前,会执行React逻辑,这样React也是有时机执行canMerge逻辑的。
如何控制canMerge逻辑
批量更新是极好滴!我们当然希望任何setState都可以被批量,关键点在于React是否有时机执行canMerge逻辑,也就是React对目标函数有没有控制权。如果没有控制权,那么就不会执行canMerge逻辑,也就不会发生setState()被react隐式合并了
通过setTimeout脱离react的控制
import React from 'react'
export default class BatchedDemo extends React.Component {
state = {
number: 0,
}
handleClick = () => {
this.setState({
number: this.state.number + 1
})
this.setState({
number: this.state.number + 2
})
this.setState({
number: this.state.number + 3
})
setTimeout(() => {
this.setState({
number: this.state.number + 4
})
this.setState({
number: this.state.number + 5
})
this.setState({
number: this.state.number + 6
})
})
}
render() {
return <button id="myButton" onClick={this.handleClick}>Num:
{this.state.number}
</button>
}
}
分析上述代码:
handleClick 是事件回调,React有时机执行canMerge逻辑,所以x为+1,+2,+3是合并的,handleClick结束之后canMerge被重新设置为false。注意这里有一个setTimeout(fn, 0)。 这个fn会在handleClick之后调用,而React对setTimeout并没有控制权,React无法在setTimeout前后执行canMerge逻辑,所以x为4,5,6是无法合并的,所以fn这里会存在3次dom-diff。React没有控制权的情况有很多: Promise.then(fn), fetch回调,xhr网络回调等等。
所以点击按钮: 3+4+5+6=18
通过unstable_batchedUpdates重回react的控制 以上代码的setTimeout中,我想让react去拿回控制权,合并代码,怎么办呢? 需要用unstable_batchedUpdates这个API 代码如下:
import React from 'react'
import { unstable_batchedUpdates as batchedUpdates } from 'react-dom'
export default class BatchedDemo extends React.Component {
state = {
number: 0,
}
handleClick = () => {
this.setState({
number: this.state.number + 1
})
this.setState({
number: this.state.number + 2
})
this.setState({
number: this.state.number + 3
})
setTimeout(() => {
batchedUpdates(() => {
this.setState({
number: this.state.number + 4
})
this.setState({
number: this.state.number + 5
})
this.setState({
number: this.state.number + 6
})
})
})
}
render() {
return <button id="myButton" onClick={this.handleClick}>Num:
{this.state.number}
</button>
}
}
打印如下:3+6=9
最后看一下这个api的伪代码:
function unstable_batchedUpdates(fn) {
this.canMerge = true
fn()
this.canMerge = false
const finalState = ...
this.setState(finaleState)
}
|