1、首先调用 setState 入口函数,入口函在这里就是重登一个分发器的角色,根据参数的不同,将其分发到不同的功能函数中去
ReactComponent.prototype.setState = function (partialState, callback) {
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
2、enqueueSetState 方法将新的 state 放进组件的状态队列里,并调用 enqueueUpdate 来处理将要更新的实例对象。
enqueueSetState: function (publicInstance, partialState) {
// 根据 this 拿到对应的组件实例
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
// 这个 queue 对应的就是一个组件实例的 state 数组
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
queue.push(partialState);
// enqueueUpdate 用来处理当前的组件实例
enqueueUpdate(internalInstance);
}
3、在 enqueueUpdate 方法中引出了一个关键的对象 —— batchingStrategy,该对象所具备的 isBatchingUpdates 属性直接决定了当下要走更新流程,还是应该排队等待;如果轮到执行,就调用 batchedUpdates 方法来直接发起更新流程。由此可以推测出,batchingStrategy 或许正是 React 内部专门用于管控批量更新的对象。
function enqueueUpdate(component) {
ensureInjected();
// 注意这一句是问题的关键,isBatchingUpdates标识着当前是否处于批量创建/更新组件的阶段
if (!batchingStrategy.isBatchingUpdates) {
// 若当前没有处于批量创建/更新组件的阶段,则立即更新组件
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
// 否则,先把组件塞入 dirtyComponents 队列里,让它“再等等”
dirtyComponents.push(component);
if (component._updateBatchNumber == null) {
component._updateBatchNumber = updateBatchNumber + 1;
}
}
setState是异步的还是同步的?
setState 并没有异步的说法,之所以会有一种异步方法的表现形式,归根结底还是因为 react 框架本身的性能机制所导致的。在 react 的生周期函数或者作用域下为异步,在原生的环境下为同步。
因为每次调用 setState 都会触发更新,异步操作是为了提高性能,将多个状态合并一起更新,减少 render 调用。
React 会将多个 setState 的调用合并为一个来执行,也就是说,当执行setState 的时候,state专用的数据不会马上更新。
一般认为,做异步设计是为了性能优化、减少渲染次数。如果每次调用 setState 都进行一次更新,那么意味着 render 函数会被频繁调用,界面重新渲染,这样效率很低,最好的办法是获取到多个更新后进行批量更新。
在开发工作中,我们可能需要同步获取到更新之后的数据,有三种常用的方法:
1、回调函数
setState 提供了一个回调函数供开发者使用,在回调函数中,我们可以实时的获取到更新之后的数据。
2、setTimeout
图标为 setState 本身并不是异步方法,其之所以会表现出异步的形式,是因为 react 框架中本身的一个性能优化机制。所以我们使用 setTimeout 越过 react 的机制,就可以令 setState 以同步的形式体现了
3、原生事件中修改状态