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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。