SETSTATEソースコード解析

SETSTATEソース

メソッドのエントリ

// 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);}

前記getInternalInstanceReadyForUpdateソースとして

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のソースコードは次のよう:

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];

enqueueUpdate含まれている反応する避免重复renderロジックを。

mountComponentそして、updateComponent初めに実行する方法は、ために呼び出されるbatchedUpdates実行する批处理アップデートを、この時間はされますisBatchingUpdatestrue、状態が今のようにマークされていること、正处于更新阶段

トランザクションがwrapper.closeを呼び出した後の後、コンポーネントの更新を処理する方法トランザクションを反応させます()。

そして、TRANSACTION_WRAPPERS含まれRESET_BATCHED_UPDATES、この中でwrapper、それが最終的に呼び出されますRESET_BATCHED_UPDATES.close()、それは最終的になり、isBatchingUpdates設定しますfalse

したがってgetInitialStatecomponentWillMountrendercomponentWillUpdatesetState意志はありませupdateComponent

しかし、中componentDidMountcomponentDidUpdate意志の。

業務

トランザクションは、wrapperカプセル化されました。

clipboard.png

wrapper含有の一対initializecloseする方法。例えばRESET_BATCHED_UPDATES

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

transcationは、次のような、包装に包装されます。

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

transactiontransaction.perform(callback, args…)入力する方法、それは最初に登録した良いを呼び出しますwrapperinitializeする方法、そして実行perform方法をcallback、そして最終的に実行closeする方法。

以下の分析transaction.perform(コールバック、引数...)

  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;
  }

アップデートコンポーネント: runBatchedUpdates

以前の分析enqueueUpdate呼び出しtransaction.perform(callback, args...)発見した後、callbackまたはenqueueUpdate方法ああ、ないでしょう死循环それは?その良いと言うまでもありませんsetStateと呼ばれるupdateComponent、自動的にビューをリフレッシュしませんか?私たちは、最初のトランザクションやトランザクションを開始する必要があります。

次のように私たちのラッパーは、2つのラッパーに登録されています。

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

RESET_BATCHED_UPDATES管理するために使用されるisBatchingUpdates状態を、我々の前にするかどうかSETSTATE即効性の分析では、前に説明しました。

ことは、FLUSH_BATCHED_UPDATESそれをやってするために使用しますか?

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_UPDATES意志でtransactionclose運用段階runBatchedUpdates、実行します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());
      }
    }
  }}

runBatchedUpdatesループdirtyComponents配列は、二つのことは主に乾燥させます。

  1. コンポーネントビューをリフレッシュするために最初に実行performUpdateIfNecessary
  2. コールバックの前に、実行をブロックします。

見てみましょう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);
    }
  },

最後の驚きソーreceiveComponentupdateComponentバー。

receiveComponent最後に、それが呼び出すupdateComponentと、updateComponentアセンブリの存在を反応させるのライフサイクル・アプローチを行います、

以下のようなcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdaterendercomponentDidUpdate

したがって、全体の更新プロセスの組み立てを完了する。

全体のプロセスレビュー:

キューに1.enqueueSetState状態入れ、コンポーネント更新するenqueueUpdateプロセスを呼び出す
コンポーネントは、更新トランザクションに現在ある場合には2を、第一の成分はdirtyComponentに格納されています。それ以外の場合は、コールbatchedUpdatesを処理します。
3.batchedUpdatesは()業務transaction.performを起動
4.三つの段階を終了、トランザクションの初期化、実行を開始します
。5.初期化:トランザクション初期化フェーズ方法はとても実行する方法がない、登録されていない
着信実行setSate:6.ファイル名を指定して実行をコールバックメソッドは、一般的にパラメータコールバックを通過しないであろう
更新isBatchingUpdatesが偽であり、中近いラッパー方法FLUSH_BATCHED_UPDATESを実行します。7.終了
全てdirtyComponents、呼updateComponentリフレッシュコンポーネントを介し近い段階、意志サイクルにおける8.FLUSH_BATCHED_UPDATES、それを実行しますpendingCallbacksは、SETSTATEはコールバックに設定されています。

理論を読んだ後、私たちは、一例で固めます。

例を見てください:

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;
 }
};

前の二つのではisBatchingUpdates、国家は、二つの出力0に更新されません。

二回以降の更新を同時に3、2出力します。

明らかに、我々は、SETSTATEシンプルなルールとして4倍にすることができます两类

  1. componentDidMountはクラスであります
  2. これら二つの異なるコールスタックを実行するためのsetTimeoutはクラスです。

のがのSETSTATEでcomponentDidMountでコールスタックを見てみましょう。

clipboard.png

setTimeoutメソッドのコールスタック内を見てください:

clipboard.png

:私たちは、中componentDidMountにフォーカスsw3eのコールスタックを見
batchedUpdates方法。

SETSTATEはもともと呼ばれる前に、それがされています处于batchedUpdates执行的事务之中

そのbatchedUpdates方法は、それを呼び出すために誰ですか?私たちは中_renderNewRootComponent法ReactMount.jsであることが判明遡及1、に行ってみましょう。

言い換えれば、アセンブリ全体は、DOMレンダリング処理に反応するの大きい取引です。

そして、それは理解しやすいですので、componentDidMount呼び出しsetState時に設定されていた2 SETSTATEの結果はすぐには反映されませんでしたので、しかし、中に入れた真ん中。batchingStrategyisBatchingUpdatestruedirtyComponents

また、これは2つのですthis.state.val印刷について説明し0、新しい状態がコンポーネントに適用されていないので、その理由を。

のないフロントがないため、二回SETSTATEのsetTimeoutでを見batchedUpdateて、コールは、フラグビットも新しいにつながった、効果はすぐに来なかったブランチ。batchingStrategyisBatchingUpdatesfalsestatedirtyComponents

換言すれば、最初のパフォーマンス、SETSTATE時間のsetTimeout、1 this.state.val。

this.state.val SETSTATEの完了後に印刷中2となります。

二SETSTATEの共感。

上記の例では、我々は、SETSTATEがある知っている可以同步更新が、それでもだけそれを理解し、直接使用を避けるようにしてください。

あなたには、いくつかのショーの運転をプレイしている場合は、書き込みコードが動作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();

私はあなたが非常に表示され、大きな胸の兄弟を言うことができます。私は今ディアオゆう、張徐高い草の墓のような古い友人がいます。

おすすめ

転載: www.cnblogs.com/geck/p/12542496.html