setstate source code analysis

setstate source

Method entry

// setState方法入口如下:
ReactComponent.prototype.setState = function (partialState, callback) {
  // 将setState事务放入队列中
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }};

replaceState:

replaceState: function (newState, callback) {
  this.updater.enqueueReplaceState(this, newState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'replaceState');
  }},
  // replaceState中取名为newState,有完全替换的含义。同样也是以队列的形式来管理的。

enqueueSetState

enqueueSetState: function (publicInstance, partialState) {
    // 先获取ReactComponent组件对象
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');

    if (!internalInstance) {
      return;
    }

    // 如果_pendingStateQueue为空,则创建它。可以发现队列是数组形式实现的
    var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);

    // 将要更新的ReactComponent放入数组中
    enqueueUpdate(internalInstance);}

Wherein getInternalInstanceReadyForUpdatethe source as

function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
  // 从map取出ReactComponent组件,还记得mountComponent时把ReactElement作为key,将ReactComponent存入了map中了吧,ReactComponent是React组件的核心,包含各种状态,数据和操作方法。而ReactElement则仅仅是一个数据类。
  var internalInstance = ReactInstanceMap.get(publicInstance);
  if (!internalInstance) {
    return null;
  }

 return internalInstance;}

enqueueUpdate source code as follows:

function enqueueUpdate(component) {
  ensureInjected();

  // 如果不是正处于创建或更新组件阶段,则处理update事务
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }

  // 如果正在创建或更新组件,则暂且先不处理update,只是将组件放在dirtyComponents数组中
  dirtyComponents.push(component);}

batchedUpdates

batchedUpdates: function (callback, a, b, c, d, e) {
  var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
  // 批处理最开始时,将isBatchingUpdates设为true,表明正在更新
  ReactDefaultBatchingStrategy.isBatchingUpdates = true;

  // The code is written this way to avoid extra allocations
  if (alreadyBatchingUpdates) {
    callback(a, b, c, d, e);
  } else {
    // 以事务的方式处理updates,后面详细分析transaction
    transaction.perform(callback, null, a, b, c, d, e);
  }}
var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function () {
    // 事务批更新处理结束时,将isBatchingUpdates设为了false
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }};var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

enqueueUpdateReact contains 避免重复renderlogic.

mountComponentAnd a updateComponentmethod of execution in the beginning, will be called to batchedUpdatesperform 批处理the update, this time will be isBatchingUpdatesset true, that is, the state is now marked as 正处于更新阶段a.

After React way transaction processing component update, after the transaction calls wrapper.close ().

And TRANSACTION_WRAPPERSincluded RESET_BATCHED_UPDATESin this wrapper, it will eventually call RESET_BATCHED_UPDATES.close(), it will eventually isBatchingUpdatesset false.

Therefore getInitialState, componentWillMount, render, componentWillUpdatein setStatewill not cause updateComponent.

But in componentDidMountand componentDidUpdateof will.

Affairs

Transaction wrapperencapsulated.

clipboard.png

A wrapperone pair of containing initializeand closemethods. For example RESET_BATCHED_UPDATES:

var RESET_BATCHED_UPDATES = {
  // 初始化调用
  initialize: emptyFunction,
  // 事务执行完成,close时调用
  close: function () {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }};

transcation is packaged in a wrapper, such as:

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

transactionThrough transaction.perform(callback, args…)method to enter, it will first call registered good wrapperin the initializemethod, and then execute performmethods callback, and finally execution closemethod.

The following analysis transaction.perform (callback, args ...)

  perform: function (method, scope, a, b, c, d, e, f) {
    var errorThrown;
    var ret;
    try {
      this._isInTransaction = true;
      errorThrown = true;
      // 先运行所有wrapper中的initialize方法
      this.initializeAll(0);

      // 再执行perform方法传入的callback
      ret = method.call(scope, a, b, c, d, e, f);
      errorThrown = false;
    } finally {
      try {
        if (errorThrown) {
          // 最后运行wrapper中的close方法
          try {
            this.closeAll(0);
          } catch (err) {}
        } else {
          // 最后运行wrapper中的close方法
          this.closeAll(0);
        }
      } finally {
        this._isInTransaction = false;
      }
    }
    return ret;
  },

  initializeAll: function (startIndex) {
    var transactionWrappers = this.transactionWrappers;
    // 遍历所有注册的wrapper
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      try {
        this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
        // 调用wrapper的initialize方法
        this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
      } finally {
        if (this.wrapperInitData[i] === Transaction.OBSERVED_ERROR) {
          try {
            this.initializeAll(i + 1);
          } catch (err) {}
        }
      }
    }
  },

  closeAll: function (startIndex) {
    var transactionWrappers = this.transactionWrappers;
    // 遍历所有wrapper
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      var initData = this.wrapperInitData[i];
      var errorThrown;
      try {
        errorThrown = true;
        if (initData !== Transaction.OBSERVED_ERROR && wrapper.close) {
          // 调用wrapper的close方法,如果有的话
          wrapper.close.call(this, initData);
        }
        errorThrown = false;
      } finally {
        if (errorThrown) {
          try {
            this.closeAll(i + 1);
          } catch (e) {}
        }
      }
    }
    this.wrapperInitData.length = 0;
  }

Update components: runBatchedUpdates

The previous analysis to the enqueueUpdatecall transaction.perform(callback, args...)after the discovery, callbackor the enqueueUpdatemethod ah, would not 死循环it? Not to say that good setStatewill be called updateComponentto automatically refresh View do? We must first start with the transaction or transactions.

Our wrapper is registered in two wrapper, as follows:

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

RESET_BATCHED_UPDATESUsed to manage the isBatchingUpdatesstate, ahead of us in the analysis of whether setState immediate effect has been explained before.

That FLUSH_BATCHED_UPDATESused to doing it?

var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)};
var flushBatchedUpdates = function () {
  // 循环遍历处理完所有dirtyComponents
  while (dirtyComponents.length || asapEnqueued) {
    if (dirtyComponents.length) {
      var transaction = ReactUpdatesFlushTransaction.getPooled();
      // close前执行完runBatchedUpdates方法,这是关键
      transaction.perform(runBatchedUpdates, null, transaction);
      ReactUpdatesFlushTransaction.release(transaction);
    }

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

FLUSH_BATCHED_UPDATESIn a will transactionof closethe operational phase runBatchedUpdates, to perform update.

function runBatchedUpdates(transaction) {
  var len = transaction.dirtyComponentsLength;
  dirtyComponents.sort(mountOrderComparator);

  for (var i = 0; i < len; i++) {
    // dirtyComponents中取出一个component
    var component = dirtyComponents[i];

    // 取出dirtyComponent中的未执行的callback,下面就准备执行它了
    var callbacks = component._pendingCallbacks;
    component._pendingCallbacks = null;

    var markerName;
    if (ReactFeatureFlags.logTopLevelRenders) {
      var namedComponent = component;
      if (component._currentElement.props === component._renderedComponent._currentElement) {
        namedComponent = component._renderedComponent;
      }
    }
    // 执行updateComponent
    ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction);

    // 执行dirtyComponent中之前未执行的callback
    if (callbacks) {
      for (var j = 0; j < callbacks.length; j++) {
        transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance());
      }
    }
  }}

runBatchedUpdatesLoop through dirtyComponentsthe array, two things mainly dry.

  1. First execution performUpdateIfNecessary to refresh the component view
  2. Blocking execution before the callback.

Let's look performUpdateIfNecessary:

performUpdateIfNecessary: function (transaction) {
    if (this._pendingElement != null) {
      // receiveComponent会最终调用到updateComponent,从而刷新View
      ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context);
    }

    if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
      // 执行updateComponent,从而刷新View。这个流程在React生命周期中讲解过
      this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
    }
  },

The last surprise saw receiveComponentand updateComponentbar.

receiveComponentFinally it will call updateComponent, and updateComponentwill perform the life-cycle approach React presence of the assembly,

Such as componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate.

Thus completing the assembly of the entire update process.

Overall Process Review:

1.enqueueSetState the state put into the queue, and calls enqueueUpdate process to update Component
2. If the component is currently in the update transaction, the first Component is stored in dirtyComponent. Otherwise, call batchedUpdates process.
3.batchedUpdates launch a transaction.perform () affairs
4. Begins a transaction initialization, run, ending three stages
5. Initialization: Transaction initialization phase method is not registered, so there is no method to execute
6. Run: Incoming execution setSate callback method, generally will not pass callback parameter
7. end: update isBatchingUpdates is false, and perform close the wrapper method FLUSH_BATCHED_UPDATES in
8.FLUSH_BATCHED_UPDATES in close stage, will cycle through all dirtyComponents, call updateComponent refresh components, and execute it pendingCallbacks, setState is set in callback.

After reading theory, we then consolidate at an example.

Look at an example:

class Example extends React.Component {
 constructor() {
   super();
   this.state = {
    val: 0
   };
}
componentDidMount() {
  this.setState({val: this.state.val + 1});
  console.log('第 1 次 log:', this.state.val);
  this.setState({val: this.state.val + 1});
  console.log('第 2 次 log:', this.state.val);

 setTimeout(() => {
  this.setState({val: this.state.val + 1});
  console.log('第 3 次 log:', this.state.val);   
  this.setState({val: this.state.val + 1});
  console.log('第 4 次 log:', this.state.val); 
 }, 0);
}
 render() {
  return null;
 }
};

In the previous two isBatchingUpdates, the State is not updated, two output 0.

Twice later updated simultaneously outputs 2, 3;

Obviously, we can be four times as setState simple rules 两类:

  1. componentDidMount is a class
  2. setTimeOut is in a class because these two perform different call stack.

Let's take a look at the call stack in componentDidMount in setState of:

clipboard.png

Look at the call stack in setTimeOut in:

clipboard.png

We look at the focus sw3e call stack in componentDidMount in:
found a batchedUpdatesmethod.

Before setState originally called, it has been 处于batchedUpdates执行的事务之中a.

That batchedUpdatesmethod is who to call it? Let us go on a retroactive one, turned out to be _renderNewRootComponent method ReactMount.js in.

In other words, the entire assembly will React to DOM rendering process is in a large transaction in the.

Then it is easy to understand: because the componentDidMountcall setStatewhen batchingStrategythe isBatchingUpdateshas been set true, so the results of the two setState did not take effect immediately, but were put into the dirtyComponentsmiddle.

This also explains the printing this.state.val are two 0reasons, because the new state has not been applied to the component.

Look twice setState setTimeOut in, because there is no front of the batchedUpdatecall, so batchingStrategythe isBatchingUpdatesflag bit false, also led to new stateeffect immediately, did not come to dirtyComponentsthe branch.

In other words, setTimeOut the first performance, setState time, this.state.val 1;

While printing after the completion of this.state.val setState becomes 2.

The second setState empathy.

By the above example, we know setState is 可以同步更新, but still try to avoid direct use, as only understand it.

If you have to play some show operation, write code to go directly to the operation this.state:

this.state.count = this.state.count + 1;
this.state.count = this.state.count + 1;
this.state.count = this.state.count + 1;
this.setState();

I can only say, big chest brother, you are very show. I have old friends like Diao Yu, Zhang Xu high grass grave now.

Guess you like

Origin www.cnblogs.com/geck/p/12542496.html