欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React.js源碼解析setState流程

 更新時間:2023年01月05日 09:45:11   作者:flyzz177  
這篇文章主要為大家介紹了React.js源碼解析setState流程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

setState

setState() 將對組件 state 的更改排入隊列批量推遲更新,并通知 React 需要使用更新后的 state 重新渲染此組件及其子組件。其實setState實際上不是異步,只是代碼執(zhí)行順序不同,有了異步的感覺。

使用方法 setState(stateChange | updater [, callback])

  • stateChange - 作為被傳入的對象,將被淺層合并到新的 state 中
  • updater - (state, props) => stateChange,返回基于 state 和 props 構(gòu)建的新對象,將被淺層合并到新的 state 中
  • callback - 為可選的回調(diào)函數(shù)

使用 setState() 改變狀態(tài)之后,立刻通過this.state拿不到最新的狀態(tài)

可以使用 componentDidUpdate() 或者 setState(updater, callback) 中的回調(diào)函數(shù) callback 保證在應(yīng)用更新后觸發(fā),通常建議使用 componentDidUpdate()

多次setState()函數(shù)調(diào)用產(chǎn)生的效果會合并

為了更好的感知性能,React 會在同一周期內(nèi)會對多個 setState() 進行批處理。通過觸發(fā)一次組件的更新來引發(fā)回流。后調(diào)用的 setState() 將覆蓋同一周期內(nèi)先調(diào)用 setState() 的值。所以如果是下一個 state 依賴前一個 state 的話,推薦給 setState() 傳 function

onClick = () => {
    this.setState({ quantity: this.state.quantity + 1 });
    this.setState({ quantity: this.state.quantity + 1 });
}
// react中,這個方法最終會變成
Object.assign(
    previousState,
    {quantity: state.quantity + 1},
    {quantity: state.quantity + 1},
    ...
)

同步 異步更新

同步更新

  • React 引發(fā)的事件處理(比如通過onClick引發(fā)的事件處理)
  • React 生命周期函數(shù)

異步更新

  • 繞過React通過 addEventListener 直接添加的事件處理函數(shù)
  • 通過 setTimeout || setInterval 產(chǎn)生的異步調(diào)用

setState()被調(diào)用之后,源碼執(zhí)行棧

react 參照版本 15.6.0

1. setState()

源碼路徑 src/isomorphic/modern/class/ReactBaseClasses.js

React組件繼承自React.Component,而setState是React.Component的方法,因此對于組件來講setState屬于其原型方法

ReactComponent.prototype.setState = function(partialState, callback) {
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
}

2. enqueueSetState(); enqueueCallback()

源碼路徑 src/renderers/shared/stack/reconciler/ReactUpdateQueue.js

這個文件導(dǎo)出了一個 ReactUpdateQueue 對象(React更新隊列)

enqueueSetState: function(publicInstance, partialState) {
    var internalInstance = getInternalInstanceReadyForUpdate(
      publicInstance,
      'setState',
    );
    if (!internalInstance) {
      return;
    }
    var queue =
      internalInstance._pendingStateQueue ||
      (internalInstance._pendingStateQueue = []);
    queue.push(partialState);
    enqueueUpdate(internalInstance);
}
enqueueCallback: function(publicInstance, callback, callerName) {
    ReactUpdateQueue.validateCallback(callback, callerName);
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
    if (!internalInstance) {
      return null;
    }
    if (internalInstance._pendingCallbacks) {
      internalInstance._pendingCallbacks.push(callback);
    } else {
      internalInstance._pendingCallbacks = [callback];
    }
    enqueueUpdate(internalInstance);
  }

3. enqueueUpdate()

源碼路徑 src/renderers/shared/stack/reconciler/ReactUpdates.js

如果處于批量更新模式,也就是 isBatchingUpdates 為 true 時,不進行state的更新操作,而是將需要更新的 component 添加到 dirtyComponents 數(shù)組中。

如果不處于批量更新模式,對所有隊列中的更新執(zhí)行 batchedUpdates 方法。

function enqueueUpdate(component) {
  ensureInjected();
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
  dirtyComponents.push(component);
  if (component._updateBatchNumber == null) {
    component._updateBatchNumber = updateBatchNumber + 1;
  }
}

4. batchedUpdates()

源碼路徑 src/renderers/shared/stack/reconciler/ReactDefaultBatchingStrategy.js

如果 isBatchingUpdates 為 true,當前正處于更新事務(wù)狀態(tài)中,則將 Component 存入 dirtyComponent 中, 否則調(diào)用 batchedUpdates 處理,發(fā)起一個 transaction.perform()。

var transaction = new ReactDefaultBatchingStrategyTransaction();
var ReactDefaultBatchingStrategy = {
  isBatchingUpdates: false,
  batchedUpdates: function(callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
    ReactDefaultBatchingStrategy.isBatchingUpdates = true;
    if (alreadyBatchingUpdates) {
      return callback(a, b, c, d, e);
    } else {
      return transaction.perform(callback, null, a, b, c, d, e);
    }
  },
};

5. transaction initialize and close

源碼路徑 src/renderers/shared/stack/reconciler/ReactDefaultBatchingStrategy.js

Transaction 中注冊了兩個 wrapper,RESET_BATCHED_UPDATESFLUSH_BATCHED_UPDATES。

initialize 階段,兩個 wrapper 都是空方法,什么都不做。

close 階段,RESET_BATCHED_UPDATES 將 isBatchingUpdates 設(shè)置為false;FLUSH_BATCHED_UPDATES 運行 flushBatchedUpdates 執(zhí)行update。

var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function() {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  },
};
var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),
};

6. 渲染更新

ReactUpdates.flushBatchedUpdates() - ReactUpdates.runBatchedUpdates() - ReactCompositeComponent.performUpdateIfNecessary() - receiveComponent() + updateComponent()

runBatchedUpdates循環(huán)遍歷dirtyComponents數(shù)組,主要干兩件事:

  • 首先執(zhí)行 performUpdateIfNecessary 來刷新組件的 view
  • 執(zhí)行之前阻塞的 callback

receiveComponent 最后會調(diào)用 updateComponent

updateComponent 中會執(zhí)行 React 組件存在期的生命周期方法,如 componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate,render, componentDidUpdate。 從而完成組件更新的整套流程

在shouldComponentUpdate之前,執(zhí)行了_processPendingState方法,該方法主要對state進行處理:

  • 如果更新隊列為null,那么返回原來的state;
  • 如果更新隊列有一個更新,那么返回更新值;
  • 如果更新隊列有多個更新,那么通過for循環(huán)將它們合并;

在一個生命周期內(nèi),在componentShouldUpdate執(zhí)行之前,所有的state變化都會被合并,最后統(tǒng)一處理。

flushBatchedUpdates(); runBatchedUpdates() 源碼路徑 src/renderers/shared/stack/reconciler/ReactUpdates.js

var flushBatchedUpdates = function() {
  while (dirtyComponents.length || asapEnqueued) {
    if (dirtyComponents.length) {
      var transaction = ReactUpdatesFlushTransaction.getPooled();
      transaction.perform(runBatchedUpdates, null, transaction);
      ReactUpdatesFlushTransaction.release(transaction);
    }
    if (asapEnqueued) {
      asapEnqueued = false;
      var queue = asapCallbackQueue;
      asapCallbackQueue = CallbackQueue.getPooled();
      queue.notifyAll();
      CallbackQueue.release(queue);
    }
  }
};
function runBatchedUpdates(transaction) {
  var len = transaction.dirtyComponentsLength;
  dirtyComponents.sort(mountOrderComparator);
  updateBatchNumber++;
  for (var i = 0; i < len; i++) {
    // dirtyComponents中取出一個component
    var component = dirtyComponents[i];
    // 取出dirtyComponent中的未執(zhí)行的callback
    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);
    }
    // 執(zhí)行updateComponent
    ReactReconciler.performUpdateIfNecessary(
      component,
      transaction.reconcileTransaction,
      updateBatchNumber,
    );
    // 執(zhí)行dirtyComponent中之前未執(zhí)行的callback
    if (callbacks) {
      for (var j = 0; j < callbacks.length; j++) {
        transaction.callbackQueue.enqueue(
          callbacks[j],
          component.getPublicInstance(),
        );
      }
    }
  }
}

performUpdateIfNecessary() 源碼路徑 src/renderers/shared/stack/reconciler/ReactReconciler.js

performUpdateIfNecessary: function(
    internalInstance,
    transaction,
    updateBatchNumber,
  ) {
    internalInstance.performUpdateIfNecessary(transaction);
  },
};

performUpdateIfNecessary() 源碼路徑 src/renderers/shared/stack/reconciler/ReactCompositeComponent.js

  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() 源碼路徑 src/renderers/shared/stack/reconciler/ReactCompositeComponent.js

事務(wù)概念

簡單地說,一個 Transaction 就是將需要執(zhí)行的 anyMethod 使用 wrapper 封裝起來,在通過 Transaction 提供的 perform 方法執(zhí)行,而在 perform 之前,先執(zhí)行所有 wrapper 中的 initialize 方法,perform 之后再執(zhí)行所有 wrapper 中的 close 方法。一組 initialize 及 close 方法稱為一個 wrapper,Transaction 支持多個 wrapper 疊加。

React 中的 Transaction 提供了一個 Mixin 方便其他模塊實現(xiàn)自己需要的事務(wù)。要實現(xiàn)自己的事務(wù),需要額外實現(xiàn)一個抽象的 getTransactionWrappers() 接口,這個接口是 Transaction 用來獲取所有 wrapper 的 initialize 和 close 方法,因此需要返回一個數(shù)組對象,每個對象分別有 key 為 initialize 和 close 的方法。

var Transaction = require('Transaction');
var emptyFunction = require('emptyFunction');
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();
}
Object.assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, {
  getTransactionWrappers: function() {
    return TRANSACTION_WRAPPERS;
  },
});

setState 流程

setState 流程還是很復(fù)雜的,設(shè)計也很精巧,避免了重復(fù)無謂的刷新組件,React大量運用了注入機制,這樣每次注入的都是同一個實例化對象,防止多次實例化

  • enqueueSetState 將 state 放入隊列中,并調(diào)用 enqueueUpdate 處理要更新的 Component
  • 如果組件當前正處于 update 事務(wù)中,則先將 Component 存入 dirtyComponent 中。否則調(diào)用 batchedUpdates 處理
  • batchedUpdates 發(fā)起一次 transaction.perform() 事務(wù)

開始執(zhí)行事務(wù)初始化,運行,結(jié)束三個階段

  • 初始化:事務(wù)初始化階段沒有注冊方法,故無方法要執(zhí)行
  • 運行:執(zhí)行 setSate 時傳入的 callback 方法,一般不會傳 callback 參數(shù)
  • 結(jié)束:執(zhí)行 RESET_BATCHED_UPDATES FLUSH_BATCHED_UPDATES 這兩個 wrapper 中的 close 方法

FLUSH_BATCHED_UPDATES 在 close 階段,flushBatchedUpdates 方法會循環(huán)遍歷所有的 dirtyComponents ,調(diào)用 updateComponent 刷新組件,并執(zhí)行它的 pendingCallbacks , 也就是 setState 中設(shè)置的 callback

組件掛載后,setState一般是通過DOM交互事件觸發(fā),如 click

  • 點擊button按鈕
  • ReactEventListener 會觸發(fā) dispatchEvent方法
  • dispatchEvent 調(diào)用 ReactUpdates.batchedUpdates
  • 進入事務(wù),init 為空, anyMethod 為 ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);

handleTopLevelImpl 是在這邊調(diào)用DOM事件對應(yīng)的回調(diào)方法

然后是setState()

  • 將state的變化和對應(yīng)的回調(diào)函數(shù)放置到 _pendingStateQueue ,和 _pendingCallback 中
  • 把需要更新的組件放到 dirtyComponents 序列中
  • 執(zhí)行 perform()
  • 執(zhí)行 close 渲染更新
dispatchEvent: function(topLevelType, nativeEvent) {
    if (!ReactEventListener._enabled) {
      return;
    }
    var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
      topLevelType,
      nativeEvent,
    );
    try {
      // Event queue being processed in the same cycle allows
      // `preventDefault`.
      ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
    } finally {
      TopLevelCallbackBookKeeping.release(bookKeeping);
    }
}

以上就是React.js源碼解析setState流程的詳細內(nèi)容,更多關(guān)于React setState流程的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • react 中父組件與子組件雙向綁定問題

    react 中父組件與子組件雙向綁定問題

    這篇文章主要介紹了react 中父組件與子組件雙向綁定問題,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-05-05
  • 用React Native制作一個簡單的游戲引擎

    用React Native制作一個簡單的游戲引擎

    今天給大家分享的是使用React Native制作一個簡單的游戲,這個游戲可以跨平臺操作,本文通過實例圖文相結(jié)合給大家介紹的非常詳細,對React Native游戲相關(guān)知識感興趣的朋友一起看看吧
    2021-05-05
  • React實現(xiàn)圖片縮放的示例代碼

    React實現(xiàn)圖片縮放的示例代碼

    這篇文章主要為大家詳細介紹了如何使用React實現(xiàn)圖片縮放的功能,文中的示例代碼講解詳細,具有一定的參考價值,感興趣的小伙伴可以了解下
    2023-10-10
  • React不使用requestIdleCallback實現(xiàn)調(diào)度原理解析

    React不使用requestIdleCallback實現(xiàn)調(diào)度原理解析

    這篇文章主要為大家介紹了React不使用requestIdleCallback實現(xiàn)調(diào)度原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11
  • react國際化react-intl的使用

    react國際化react-intl的使用

    這篇文章主要介紹了react國際化react-intl的使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • react-native滑動吸頂效果的實現(xiàn)過程

    react-native滑動吸頂效果的實現(xiàn)過程

    這篇文章主要給大家介紹了關(guān)于react-native滑動吸頂效果的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用react-native具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • react實現(xiàn)換膚功能的示例代碼

    react實現(xiàn)換膚功能的示例代碼

    這篇文章主要介紹了react實現(xiàn)換膚功能的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • React的diff算法核心復(fù)用圖文詳解

    React的diff算法核心復(fù)用圖文詳解

    這篇文章主要為大家介紹了React的diff算法核心復(fù)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • ReactNative Image組件使用詳解

    ReactNative Image組件使用詳解

    本篇文章主要介紹了ReactNative Image組件使用詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • React中使用TS完成父組件調(diào)用子組件的操作方法

    React中使用TS完成父組件調(diào)用子組件的操作方法

    由于在項目開發(fā)過程中,我們往往時需要調(diào)用子組件中的方法,這篇文章主要介紹了React中使用TS完成父組件調(diào)用子組件,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07

最新評論