乐趣区

日常抄书之一次性弄懂setState

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

退出移动版