setState执行机制

当this.setState()被调用时,React会重新调用render方法来重新绘制UI

异步更新

setState通过一个队列机制实现state的更新
当执行setState时,会将需要更新的state合并后放入状态队列,而不是立刻更新
队列机制可以高效的批量更新state,如果不通过setState直接修改this.state的值,将不会放入状态队列中,当下次调用setState并对状态队列进行合并时,将会忽略之前的直接被修改的state,而造成无法预知的错误

setState流程还是很复杂的,设计也很精巧,避免了重复无谓的刷新组件。它的主要流程如下

  1. enqueueSetState将state放入队列中,并调用enqueueUpdate处理要更新的Component
  2. 如果组件当前正处于update事务中,则先将Component存入dirtyComponent中。否则调用batchedUpdates处理。
  3. batchedUpdates发起一次transaction.perform()事务
  4. 开始执行事务初始化,运行,结束三个阶段
    1. 初始化:事务初始化阶段没有注册方法,故无方法要执行
    2. 运行:执行setSate时传入的callback方法,一般不会传callback参数
    3. 结束:更新isBatchingUpdates为false,并执行FLUSH_BATCHED_UPDATES这个wrapper中的close方法
  5. FLUSH_BATCHED_UPDATES在close阶段,会循环遍历所有的dirtyComponents,调用updateComponent刷新组件,并执行它的pendingCallbacks, 也就是setState中设置的callback。

循环调用风险

当调用setState时,实际上会执行enqueueSetState方法,并对partialState 以及_pendingStateQueue更新队列进行合并操作,最终通过enqueueSetState执行state更新
而performUpdateIfNecessary方法会获取_pendingElement、_pendingStateQueue、_pendingForceUpdate 并调用receiveComponent和updateComponent方法进行组件更新
如果在shouldComponentUpdate 或 componentWillUpdate方法中调用setSate
此时this._pendingStateQueue != null 则performUpdateIfNecessary方法就会调用updateComponent方法进行组件更新

performUpdateIfNecessary: function (transaction) {
  if (this._pendingElement != null) {
    // receiveComponent会最终调用到updateComponent,从而刷新View
    ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context);
  } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
    // 执行updateComponent,从而刷新View。
    this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
  } else {
    this._updateBatchNumber = null;
  }
}

但updateComponent方法又会调用shouldComponentUpdate 和 componentWillUpdate方法
因此造成循环调用 使得浏览器内存占满奔溃

调用栈

import React,{Component} from 'react';

class Example extends Component {
    constructor(){
        super();
        this.state = {
            val:0
        }
    }
    componentDidMount(){
        
        this.setState({
            val:this.state.val + 1
        })
        console.log(this.state.val) // 第1次输出 0

        this.setState({
            val:this.state.val + 1
        })
        console.log(this.state.val) // 第2次输出 0
        
        setTimeout(() => {
            this.setState({
                val:this.state.val + 1
            })
            console.log(this.state.val) // 第3次输出 2

            this.setState({
                val:this.state.val + 1
            })
            console.log(this.state.val) // 第4次输出 3
        })
    }

}
                            this.setState()
                                ||
                                ||
                                \/
                        newState存入pending队列
                                ||
                                ||
                                \/
                        调用enqueueUpdate
                                ||
                                ||
                                \/
                        是否处于批量更新模式
                        ||              ||
                        || Y            || N
                        \/              \/
                   将组件保存到       遍历dirtyComponrnts
                dirtyComponents      调用updateComponent
                                     更新pending state or props

                            setState简化调用栈

enqueueUpdate 的代码如下

function enqueueUpdate(component){
    ensureInjected();
    // 如果不处于批量更新模式
    if(!batchingStrategy.isBatchingUpdates){
        batchingStrategy.batchedUpdates(enqueueUpdate,component);
        return;
    }
    // 如果处于批量更新模式 则将组件保存在 dirtyComponents中
    dirtyComponents.push(component);
}

如果 isBatchingUpdates 为true,则对队列中的更新执行 batchedUpdates 方法
否则只把当前的组件(即调用了setState的组件)放入dirtyComponents数组中
例子中4次setState调用的实现之所以不同 这里逻辑判断起了关键作用

  • 11
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值