React-源码解析-setState运行机制

本文深入探讨React中setState方法的工作原理,包括其异步处理机制、更新队列的维护及批量更新策略。通过代码注释形式详细说明从调用setState到最终触发组件更新的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以代码注释的方式整理setState的执行流程


用class 的方式创建React组件,需要继承React.Component,而setState()方法就绑定在他的原型上面

  • 发现setState()上有两个参数
  • 第一个参数是需要修改的state对象,或者是函数(preState:更新前的sate,props:当前的属性)
  • 第二个参数是修改完state执行的回调函数

ReactComponent.prototype.isReactComponent = {};
ReactComponent.prototype.setState = function(partialState, callback) {
  //这里的this.updater就是ReactUpdateQueue,--- this是组件的实例
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};
复制代码

  • 调用了this.updater.enqueueSetState(this, partialState);
  • 主要是初始化更新队列,并将需要修改的state加入到更新队列中
enqueueSetState: function(publicInstance, partialState) {
    //获取当前实例
    //实例中有两个非常重要的属性
    //_pendingStateQueue(待更新队列) 与 _pendingCallbacks(更新回调队列)
    var internalInstance = getInternalInstanceReadyForUpdate( publicInstance, 'setState',  );
    if (!internalInstance) {
      return;
    }
    //初始化待更新队列
    var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);
    enqueueUpdate(internalInstance);
},
复制代码

  • 调用enqueueUpdate(internalInstance)时调用 ReactUpdates.enqueueUpdate(internalInstance);
function enqueueUpdate(internalInstance) {
  ReactUpdates.enqueueUpdate(internalInstance);
}
复制代码

  • 这个方法很关键
  • 这个方法的主要内容就是当 isBatchingUpdates = false 时,开启批量更新模式
  • 否则就会将组件对象加入到批量更新组件的数组中
    • 需要注意的是 isBatchingUpdates == true 就直接将组件push数组.记住这个判断
function enqueueUpdate(component) {
    //如果不处于批量更新模式
    if (!batchingStrategy.isBatchingUpdates) {
        batchingStrategy.batchedUpdates(enqueueUpdate, component);
        return;
    }
    //如果处于批量更新模式,则将该组件保存在dirtyComponents数组中
    dirtyComponents.push(component);
}
复制代码

  • 当没有开启批量更新模式的时候调用 batchingStrategy.batchedUpdates(enqueueUpdate, component);
  • batchedUpdates() 其实是ReactDefaultBatchingStrategy对象的一个函数
  • 不管开没开起批量更新模式,都会先保存之前的状态,然后开启更新模式
  • 如果没开启更新模式,就使用事物的方式调用回调函数
/**
 * 调用batchingStrategy.batchedUpdates(enqueueUpdate, component);
 * ReactDefaultBatchingStrategy实际上是一个批量更新策略
 */
var ReactDefaultBatchingStrategy = {
    isBatchingUpdates: false,
    batchedUpdates: function(callback, a, b, c, d, e) {
		//第一次的时候为false
        var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
		//然后改成true,体会下什么时候在改成true
        ReactDefaultBatchingStrategy.isBatchingUpdates = true;

        if (alreadyBatchingUpdates) {
            return callback(a, b, c, d, e);
        } else {
            return transaction.perform(callback, null, a, b, c, d, e);
        }
    },
};
复制代码
  • transaction.perform()
  • 这里涉及到了事物的概念
  • Transaction会给每一个需要执行的方法包装一个wrapper,而这个wrapper内有两个方法 initialize 与 close 当要执行目标方法前要先执行 initialize() 然后才是目标方法 之后再执行close 而这里 initialize() 是空函数
  • 简而言之就是wrapper(initialize,perform,close)顺序执行,而initialize还是个空函数,所以执行完perform,再执行close

  • RESET_BATCHED_UPDATES,FLUSH_BATCHED_UPDATES 定义了两个wrapper
  • RESET_BATCHED_UPDATES 负责在close阶段重置 ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  • FLUSH_BATCHED_UPDATES 负责在close阶段 执行ReactUpdates.flushBatchedUpdates.bind(ReactUpdates);
  • 然后把这两个放到一个数组中,在ReactDefaultBatchingStrategyTransaction的原型上绑定getTransactionWrappers用于返回wrapper的数组;
var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function() {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  },
};
var  FLUSH_BATCHED_UPDATES= {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),
};

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

function ReactDefaultBatchingStrategyTransaction() {
  this.reinitializeTransaction();
}
/**
 *  把wrapper绑定到ReactDefaultBatchingStrategyTransaction的原型上
 */
Object.assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, {
  getTransactionWrappers: function() {
    return TRANSACTION_WRAPPERS;
  },
});
复制代码

  • FLUSH_BATCHED_UPDATES.close=>ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
  • 这个方法再迭代更新组件数组,然后又以事物的方式调用了runBatchedUpdates()方法
  • 执行完回来释放实例
var flushBatchedUpdates = function() {
    while (dirtyComponents.length || asapEnqueued) {
        if (dirtyComponents.length) {
            var transaction = ReactUpdatesFlushTransaction.getPooled();
            //又以事物的形式调用了 runBatchedUpdates()
            transaction.perform(runBatchedUpdates, null, transaction);
            //释放实例
            ReactUpdatesFlushTransaction.release(transaction);
        }

        if (asapEnqueued) {
            asapEnqueued = false;
            var queue = asapCallbackQueue;
            asapCallbackQueue = CallbackQueue.getPooled();
            queue.notifyAll();
            CallbackQueue.release(queue);
        }
    }
};
复制代码

  • 根据名称就知道这是个执行批量更新的操作
  • 这个方法主要就是调用了组件的performUpdateIfNecessary()
function runBatchedUpdates(transaction) {
  var len = transaction.dirtyComponentsLength;
  dirtyComponents.sort(mountOrderComparator);//排序,保证更新的顺序
  updateBatchNumber++;
  for (var i = 0; i < len; i++) {
    var component = dirtyComponents[i];
    //在组件中获取回调函数
    var callbacks = component._pendingCallbacks;
    component._pendingCallbacks = null;

    var markerName;
    if (ReactFeatureFlags.logTopLevelRenders) {
      var namedComponent = component;
      if (component._currentElement.type.isReactTopLevelWrapper) {
        namedComponent = component._renderedComponent;
      }
      markerName = 'React update: ' + namedComponent.getName();
      console.time(markerName);
    }
    //经过一些列处理 其实就是调用了component.performUpdateIfNecessary,也就是自定义组件ReactCompositeComponent.performUpdateIfNecessary()
    ReactReconciler.performUpdateIfNecessary( component, transaction.reconcileTransaction, updateBatchNumber, );

    if (markerName) {
      console.timeEnd(markerName);
    }

    if (callbacks) {
      for (var j = 0; j < callbacks.length; j++) {
        transaction.callbackQueue.enqueue( callbacks[j], component.getPublicInstance());
      }
    }
  }
}
复制代码

  • 发现其实是调用了组件实例的performUpdateIfNecessary()

/**
 * 然后关键 ReactReconciler.performUpdateIfNecessary( component, transaction.reconcileTransaction, updateBatchNumber, );
 */
  performUpdateIfNecessary: function(internalInstance,transaction,updateBatchNumber,) {
    if (internalInstance._updateBatchNumber !== updateBatchNumber) {
      return;
    }
    internalInstance.performUpdateIfNecessary(transaction);
  },
复制代码

  • 然后又回到了ReactCompositeComponent自定义组件
  /**
   * internalInstance.performUpdateIfNecessary(transaction);
   *  调用了实例的performUpdateIfNecessary方法,这个实例就是自定义组件的实例
   */
    // 如果存在 _pendingElement、_pendingStateQueue和_pendingForceUpdate,则更新组件
  performUpdateIfNecessary: function(transaction) {
    if (this._pendingElement != null) {
      ReactReconciler.receiveComponent(this, this._pendingElement,transaction,this._context,);
    } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
      this.updateComponent(transaction,this._currentElement,this._currentElement,this._context,this._context,);
    } else {
      this._updateBatchNumber = null;
    }
  },
复制代码

  • 跳到了非常熟悉的updateComponent方法
updateComponent: function(transaction,prevParentElement,nextParentElement,prevUnmaskedContext,nextUnmaskedContext,) {
  var inst = this._instance;
  var willReceive = false;
  var nextContext;

  // 上下文是否改变
  if (this._context === nextUnmaskedContext) {
    nextContext = inst.context;
  } else {
    nextContext = this._processContext(nextUnmaskedContext);
    willReceive = true;
  }

  var prevProps = prevParentElement.props;
  var nextProps = nextParentElement.props;

  // 新旧属性不同
  if (prevParentElement !== nextParentElement) {
    willReceive = true;
  }
  //新旧属性不同,并且存在componentWillReceiveProps,就执行componentWillReceiveProps()
  if (willReceive && inst.componentWillReceiveProps) {
    inst.componentWillReceiveProps(nextProps, nextContext);
  }
  //将新的state合并到更新队列中,此时的nextState是最新的state
  var nextState = this._processPendingState(nextProps, nextContext);
  var shouldUpdate = true;
  //根据更新队列和shouldComponentUpdate的状态来判断是否需要更新组件
  if (!this._pendingForceUpdate) {
    if (inst.shouldComponentUpdate) {
      shouldUpdate = inst.shouldComponentUpdate(nextProps,nextState,nextContext,);
    } else {
      if (this._compositeType === CompositeTypes.PureClass) {
        shouldUpdate =!shallowEqual(prevProps, nextProps) ||!shallowEqual(inst.state, nextState);
      }
    }
  }

  this._updateBatchNumber = null;
  if (shouldUpdate) {
    //重置更新队列
    this._pendingForceUpdate = false;
    //即将更新this.props,this.state,和this.context
    this._performComponentUpdate(nextParentElement,nextProps, nextState,nextContext,transaction,nextUnmaskedContext,);
  } else {
    // 如果确定组件不更新,仍然要这是props和state
    this._currentElement = nextParentElement;
    this._context = nextUnmaskedContext;
    inst.props = nextProps;
    inst.state = nextState;
    inst.context = nextContext;
  }
},
/**
 * 调用了this._processPendingState(nextProps, nextContext)
 */
_processPendingState: function(props, context) {
  var inst = this._instance;
  var queue = this._pendingStateQueue;
  var replace = this._pendingReplaceState;
  this._pendingReplaceState = false;
  this._pendingStateQueue = null;
  //如果队列为null,返回原state
  if (!queue) {
    return inst.state;
  }
  //如果队列中有一个更新就返回这个更新值
  if (replace && queue.length === 1) {
    return queue[0];
  }
  //如果队列中有多个更新,就将他们合并
  var nextState = Object.assign({}, replace ? queue[0] : inst.state);
  for (var i = replace ? 1 : 0; i < queue.length; i++) {
    var partial = queue[i];
    //这里就是setState的第一个参数的另外一种情况,可以传入函数
    //这里用函数的形式,合并到了nextState上,就获取了最新的state值
    Object.assign(
      nextState,
      typeof partial === 'function'
        ? partial.call(inst, nextState, props, context)
        : partial,
    );
  }
  //返回了最新的state的值
  return nextState;
},
复制代码

  • 在执行完componentWillUpdate 之后才能获取最新的state值
 /**
   * 当组件需要更新时,调用
   */
  _performComponentUpdate: function(nextElement,nextProps,nextState,nextContext,transaction,unmaskedContext) {
    var inst = this._instance;
    var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
    var prevProps;
    var prevState;
    var prevContext;
    //如果存在componentDidUpdate,则将当前的props,state和context保存一份
    if (hasComponentDidUpdate) {
      prevProps = inst.props;
      prevState = inst.state;
      prevContext = inst.context;
    }
    //执行componentWillUpdate
    if (inst.componentWillUpdate) {
      inst.componentWillUpdate(nextProps, nextState, nextContext);
    }
    //更新this.props,this.state,this.context
	//这个时候才开始给实例的props和state,context赋值
    this._currentElement = nextElement;
    this._context = unmaskedContext;
    inst.props = nextProps;
    inst.state = nextState;
    inst.context = nextContext;
    //渲染组件
    this._updateRenderedComponent(transaction, unmaskedContext);
    //当组件完成更新后,如果存在ComponentDidUpdate,则调用
    if (hasComponentDidUpdate) {
      transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst,prevProps, prevState,prevContext,),inst,);
    }
  },
/**
  *	这里runBatchedUpdates的事物才执行完毕,也就是说在componentDidUpdate()之后调用事物的close,
  */
复制代码

setState的第二个参数问题

ReactComponent.prototype.setState = function(partialState, callback) {
	//这里的this.updater就是ReactUpdateQueue,--- this是组件的实例
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};
/**
 * 跟enqueueSetState 类似
 * 执行这个方法最主要的就是将回调函数绑定到了internalInstance._pendingCallbacks上
 */
enqueueCallback: function(publicInstance, callback, callerName) {
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
    if (!internalInstance) {
      return null;
    }
    //就是初始化_pendingCallbacks
    if (internalInstance._pendingCallbacks) {
      internalInstance._pendingCallbacks.push(callback);
    } else {
      internalInstance._pendingCallbacks = [callback];
    }
    
    enqueueUpdate(internalInstance);
},
/**
 * 最后又跳到了这里,就不重要了
 */
function enqueueUpdate(internalInstance) {
  ReactUpdates.enqueueUpdate(internalInstance);
}
/**
 * 回调函数是在setState执行完之后再执行的
 */

/**
 *  该方法用于迭代dirtyComponents
 */
var flushBatchedUpdates = function() {
    while (dirtyComponents.length || asapEnqueued) {
        if (dirtyComponents.length) {
            var transaction = ReactUpdatesFlushTransaction.getPooled();
            //又以事物的形式调用了 runBatchedUpdates()
            transaction.perform(runBatchedUpdates, null, transaction);
            //释放实例
            ReactUpdatesFlushTransaction.release(transaction);
        }

        if (asapEnqueued) {
            asapEnqueued = false;
            var queue = asapCallbackQueue;
            asapCallbackQueue = CallbackQueue.getPooled();
            queue.notifyAll();
            CallbackQueue.release(queue);
        }
    }
};

/**
 * 以下是ReactUpdatesFlushTransaction定义
 * 在close的时候发现调用了this.callbackQueue.notifyAll();
 * 而notifyAll方法是取出回调函数开始执行
 */
var UPDATE_QUEUEING = {
  initialize: function() {
    this.callbackQueue.reset();
  },
  close: function() {
    this.callbackQueue.notifyAll();
  },
};

var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];

function ReactUpdatesFlushTransaction() {
  this.reinitializeTransaction();
  this.dirtyComponentsLength = null;
  this.callbackQueue = CallbackQueue.getPooled();
  this.reconcileTransaction = ReactUpdates.ReactReconcileTransaction.getPooled(true,);
}

  /**
   * 执行回调函数
   */
  notifyAll() {
    var callbacks = this._callbacks;
    var contexts = this._contexts;
    var arg = this._arg;
    if (callbacks && contexts) {
      this._callbacks = null;
      this._contexts = null;
      for (var i = 0; i < callbacks.length; i++) {
        callbacks[i].call(contexts[i], arg);
      }
      callbacks.length = 0;
      contexts.length = 0;
    }
  }
复制代码

setState的异步处理


这个可以追踪源码理解,经过上面的流程那么这个就不难解释了

  1. 直接调用this.setState()
  • 因为在整个生命周期中就是一个事物操作,所以标识位isBatchingUpdates = true,所以流程到了enqueueUpdate()时,实例对象都会加入到dirtyComponents 数组中
  • 接着执行componentWillMount()
  • 将所有的setState操作进行合并(相同的操作合并成一个)
  • 渲染
  • 执行componentDidMount()
  • 事物结束,调用close方法->调用runBatchUpdate()
  • 继续走更新流程
  1. 使用setTimeout()方法调用
  • setTimeout()没在事物中 isBatchingUpdates = false 所以就直接走直接更新操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值