平常抄書之一次性弄懂setState

1.前言

React是經過管理狀態來實現對組件的管理。那麼React是如何控制組件的狀態,又是如何利用狀態來管理組件的呢?數組

在React中是經過this.setState()來更新state。當調用this.setState()的時候,React會從新調用render方法來從新渲染UI。bash

2.異步setState

setState是一個異步操做。setState是經過隊列機制實現state 更新。當執行setState會將須要更新的state合併後放入 狀態隊列,而不會馬上更新this.state。app

//將新的state合併到狀態更新隊列中
var nextState = this._processPendingState(nextProps, nextContent)
//根據更新隊列和shouldComponentUpdate的狀態來判斷是否須要更新組件
var shouldUpdate = this._pendingForceUpdate || !inst.shouldComponentUpdate || inst.shouldComponentUpdate(nextProps, nextState, nextContext)
複製代碼

3.setState循環調用風險

不要在shouldComponentUpdatecomponentWillUpdate中調用setState,否則會出現死循環。異步

在調用setState時,實際上回執行enqueueSetState方法,並對partialState_pendingStateQueue更新隊列進行合併操做。最終經過enqueueUpdate執行state更新。ui

performUpdateIfNecessary方法會獲取 _pendingElement_pendingStateQueue_pendingForceUpdate,並調用receiveComponentupdateComponent方法進行組件更新。this

若是在componentWillUpdateshouldComponentUpdate中調用setState,此時 _pendingStateQueue!==nullperformUpdateIfNecessary會調用updateComponent進行組件更新,而updateComponent又會調用shouldComponentUpdateshouldComponentUpdate,這樣就會致使循環調用。spa

接下來看下setState源碼:prototype

ReactComponent.prototype.setState = function(partialState, callback) {
    //調用enqueueSetState,將setState事務放進隊列中
    //partialState能夠傳object,也能夠穿function,他會產生新的state以一種
    //Object.assign()的方式跟舊的state進行合併。
    this.updater.enqueueSetState(this, partialState)
    if(callback) {
        this.updater.enqueueCallback(this, callback, 'setState')
    }
}

//實際經過enqueueSetState執行。
//1. 將新的state放進數組
//2. 用enqueueUpdate來處理將要更新的實例對象
enqueueSetState: function(publicInstance, partialState) {
    //獲取當前組件的instance
    var internalInstance = getInternalInstanceReadyForUpdate(
        publicInstance,
        'setState'
    )
    
    if(!internalInstance) return
    
    //更新隊列合併操做
    var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = [])
    
    //partialState能夠理解爲以前的state
    queue.push(partialState)
    //最終經過enqueueUpdate更新,將要更新的component instance放入一個隊列
    enqueueUpdate(internalInstance)
}

//若是存在_pendingElement、_pendingStateQueue和_pendingForceUpdate,則更新組件
performUpdateIfNecessary: function(transaction) }
    if(this._pendingElement != null) {
       ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context) 
    }
    if(this._pendingStateQueue !== null || this._pendingForceUpdate) {
        this.updateComponent(transaction, this._currentElement, this._currentElement, this._context)
    }
    
}
複製代碼

4.setState調用棧

function enqueueUpdate(component) {
    ensureInjected();
    
    //若是不處於批量更新模式
    if(!batchingStrategy.isBatchingUpdates) {
        batchingStrategy.batchedUpdates(enqueueUpdate, component)
        return
    }
    //若是處於批量更新模式
    dirtyComponents.push(component)
}
複製代碼
//batchingStrategy
var ReactDefaultBatchingStrategy = {
    isBatchingUpdates: false,
    batchedUpdates: function(callback, a, b, c, d, e) {
        var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates
        ReactDefaultBatchingStrategy.isBatchingUpdates = true
        
        if(alreadyBatchingUpdates) {
            callback(a,b,c,d,e)
        }else {
            transaction.perform(callback, null, a, b, c, d, e)
        }
    }
}
複製代碼

batchedUpdate中有一個transaction.perform調用。這就是事務的概念。3d

5. 事務

事務就是將須要執行的方法使用wrapper封裝起來,再經過事務提供的perform方法執行。而在perform以前,先執行所wrapper中的initialize方法,執行完perform以後再執行全部的close方法。一組initialize以及close方法稱爲一個wrappercode

到實現中,事務提供一個mixin方法供其餘模塊實現本身須要的事務。而要使用事務的模塊除了須要把mixin混入本身的事務實現以外,還要額外實現一個抽象getTransactionWrap接口。這個接口用來獲取須要封裝的前置方法(initialize)和收尾方法(close)。所以它須要返回一個數組的對象,這個對象分別有key爲initializeclose的方法。

var Transaction = require('./Transaction')
//咱們本身定義的事務
var MyTransaction = function() {}

Object.assign(MyTransaction.prototype, Transaction.mixin, {
    getTransactionWrap: function() {
        return [{
            initialize: function() {
                console.log("before method perform")
            },
            close: function() {
                console.log("after method perform")
            }
        }]
    }
})

var transaction = new MyTransaction()
var testMethod = function() {
    console.log('test')
}
transaction.perform(testMethod)
//打印結果以下:
//before method perform
//test
//after method perform
複製代碼
相關文章
相關標籤/搜索