1.前言
React是通過管理狀態來實現對組件的管理。那麼React是如何控制組件的狀態,又是如何利用狀態來管理組件的呢?
在React中是通過this.setState()來更新state。當調用this.setState()的時候,React會重新調用render方法來重新渲染UI。
2.異步setState
setState是一個異步操作。setState是通過隊列機制實現state 更新。當執行setState會將需要更新的state合併後放入 狀態隊列,而不會立刻更新this.state。
//將新的state合併到狀態更新隊列中
var nextState = this._processPendingState(nextProps, nextContent)
//根據更新隊列和shouldComponentUpdate的狀態來判斷是否需要更新組件
var shouldUpdate = this._pendingForceUpdate || !inst.shouldComponentUpdate || inst.shouldComponentUpdate(nextProps, nextState, nextContext)
3.setState循環調用風險
不要在shouldComponentUpdate和componentWillUpdate中調用setState,不然會出現死循環。
在調用setState時,實際上回執行enqueueSetState方法,並對partialState、_pendingStateQueue更新隊列進行合併操作。最終通過enqueueUpdate執行state更新。
而performUpdateIfNecessary方法會獲取 _pendingElement、_pendingStateQueue、_pendingForceUpdate,並調用receiveComponent和updateComponent方法進行組件更新。
如果在componentWillUpdate和shouldComponentUpdate中調用setState,此時 _pendingStateQueue!==null 則 performUpdateIfNecessary會調用updateComponent進行組件更新,而updateComponent又會調用shouldComponentUpdate和shouldComponentUpdate,這樣就會導致循環調用。
接下來看下setState源碼:
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調用。這就是事務的概念。
5. 事務
事務就是將需要執行的方法使用wrapper封裝起來,再通過事務提供的perform方法執行。而在perform之前,先執行所wrapper中的initialize方法,執行完perform之後再執行所有的close方法。一組initialize以及close方法稱爲一個wrapper。
到實現中,事務提供一個mixin方法供其他模塊實現自己需要的事務。而要使用事務的模塊除了需要把mixin混入自己的事務實現之外,還要額外實現一個抽象getTransactionWrap接口。這個接口用來獲取需要封裝的前置方法(initialize)和收尾方法(close)。因此它需要返回一個數組的對象,這個對象分別有key爲initialize和close的方法。
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