react中setState()的执行策略是什么?如何合并的那?如何控制合并?

学习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 
    }

    // 下面是真正的更新: dom-diff, lifeCycle...
    ...
}

然后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

		 // 通过this.updateQueue合并出finalState
	    const finalState = ...  
	    // 此时canMerge 已经为false 故而走入时机更新逻辑
	    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(() => {
            //通过这个api,让react拿回控制权,执行canMerge逻辑
            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.updateQueue合并出finalState
    this.setState(finaleState)
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值