源碼看React setState漫談(一)

一張圖看懂React setState操做

網上關於react setState的結論很多,好比:react

  • setState不會馬上改變React組件中state的值;
  • 屢次setState函數調用產生的效果會合並。

但你是否真的瞭解setState背後的機制?真的是setState觸發的刷新嗎?
廢話不說,先上圖
圖片描述app

組件掛載後,setState通常是經過DOM交互事件觸發。這裏以click爲例,其餘也同樣。函數

代碼很簡單this

import React, {Component} from 'react';
class MyInfo extends Component{
    constructor(props,context){
        super(props,context);
        this.state = {
            age:1
        }
    }

     _grow(age){
        age++
        this.setState({
            age:age
        })
    }

    render(){
        const {age} = this.state
        return (
            <div>
                個人年齡是{age}
                <button onClick={this._grow.bind(this,age)}>點擊漲一歲</button>
            </div>
        )
    }
}

export default MyInfo;

咱們點擊button按鈕時,到底發生了什麼?ReactEventListener會觸發dispatchEvent方法。(具體怎麼觸發是事件機制的事,這裏不深究)spa

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

能夠看到這裏有個ReactUpdates.batchedUpdates方法。咱們跟進去看看prototype

function batchedUpdates(callback, a, b, c, d, e) {
  ensureInjected();
  return batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}

能夠發現這裏調用了batchingStrategy的方法。這又是什麼鬼,其實這是注入進來的ReactDefaultBatchingStragy這裏插一句,React大量運用了注入機制,這樣每次注入的都是同一個實例化對象,防止屢次實例化。
到這邊就已經開啓了批量更新模式
繼續看,code

batchedUpdates: function(callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

    // The code is written this way to avoid extra allocations
    if (alreadyBatchingUpdates) {
      return callback(a, b, c, d, e);
    } else {
      return transaction.perform(callback, null, a, b, c, d, e);
    }
  },

transaction.perform執行了一個事務。事務其餘文章說的不少我就不詳細解釋了。大概就是,transaction在執行perform以前會執行特性的initialize方法,而後執行傳進去的callback,以後會執行close方法,是否是似曾相識?沒錯,高階函數或者高階組件都是這路數。
ReactDefaultBatchingStragy裏能夠發現orm

var transaction = new ReactDefaultBatchingStrategyTransaction();

//在事務結束時清理一下標識
var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function() {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  },
};

// 在事務結束時執行flushBatchedUpdates方法,這個方法就是 state 更新的核心代碼了。
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;
  },
});

能夠發現這個transition有兩個wrapper,主要看FLUSH_BATCHED_UPDATES對象

目前走完了圖中的第一行,有點暈的能夠對着圖回顧一下。blog

-

是否是發現哪裏不對?是的,到如今咱們的setState還沒執行呢!
接着上文,咱們首先看看transition的兩個initailize方法,發現時兩個空函數。跳過
接着就是perform須要執行的邏輯了。再次放出代碼

ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);

也就是執行這邊的handleTopLevelImpl。正是在這邊調用DOM事件對應的回調方法。也就是例子中_grow在這時候調用。
而後是setState方法。這裏和大部分書和文章說的差很少。拋開細節就是將state的變化和對應的回調函數放置到_pendingStateQueue,和_pendingCallback中。
而後把須要更新的組件放到dirtyComponents序列中。
重點來了:
注意注意!!!!
setState歷來不負責更新操做。它的工做只是把state,和callback放進序列,而且把要更新的組件放到dirtyComponents序列
還記得嗎?咱們還在ReactDefalutBatchingStragy的事務中,perform執行完了,還要執行close。
真正執行更新方法的是close裏面的flushBatchedUpdates。鑑於文章長度,其餘的能夠看圖理解

相關文章
相關標籤/搜索