本文基于React15.6.0介绍setState机制,同时包括常见的setState问题比如setState的批量更新,同步异步等。希望各位能有所收获。
1,React中的事务(transaction)
介绍setState机制之前,我们先了解一下React中的 事务(transaction) (别怕,简单)
事务(transaction):关于react中的事务,本质就是将 目标方法 进行包装,目标方法 执行前先执行所有包装的initialize方法,之后执行 目标方法 ,最后执行行所有包装的close方法。
通过图直观的感受事务(transaction)的运作方式
通过代码直观的感受事务(transaction)的运作方式
class Transaction {
constructor() {
this.transactionWrappers = []
}
// 添加包装方法[{init1,close1},{init2,close2},......]
setTransactionWrappers(transactionWrappers) {
this.transactionWrappers = transactionWrappers
}
// 执行事务
perform(method) {
// 首先执行所有包装方法中的init
this.transactionWrappers.forEach(({ initialize, close }) => initialize())
// 执行当前目标方法
method()
// 首先执行所有包装方法中的close
this.transactionWrappers.forEach(({ initialize, close }) => close())
}
}
const wrapper1 = {
initialize: () => console.log('wrapper1-initialize'),
close: () => console.log('wrapper1-close')
}
const wrapper2 = {
initialize: () => console.log('wrapper2-initialize'),
close: () => console.log('wrapper2-close')
}
// 目标方法
const showCurrentTime = () => console.log(new Date().toGMTString())
const trasaction = new Transaction()
trasaction.setTransactionWrappers([wrapper1, wrapper2])
trasaction.perform(showCurrentTime)
// 输出:
// wrapper1-initialize
// wrapper2-initialize
// Tue, 31 Aug 2021 09:08:12 GMT
// wrapper1-close
// wrapper2-close
通过 react源码中提供的事务流程图 的运作方式
相信看到这里已经理解react中的事务到底是如何运作,那么继续:
2,React批量更新策略事务(ReactDefaultBatchingStrategyTransaction)
现在将介绍 React批量更新策略事务 ,这个也是为我们搞清楚setState机制做铺垫(别怕,简单)
我们知道一个事务的wrapper是什么,那么我们就清楚这个事务是如何运行,所以这里我们看看 React批量更新策略事务 的wrapper方法做了什么。
// React批量更新策略事务中的两个wrapper:
// FLUSH_BATCHED_UPDATES
// RESET_BATCHED_UPDATES
// wrapper1:
FLUSH_BATCHED_UPDATES = {
// initialize:空函数,什么都不做
initialize: emptyFunction,
// close:调用flushBatchedUpdates,该函数作用是批量更新组件
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),
}
// wrapper2:
RESET_BATCHED_UPDATES = {
// initialize:空函数,什么都不做
initialize: emptyFunction,
// close:将 全局 React批量更新标识(ReactDefaultBatchingStrategy.isBatchingUpdates)重置为false
close: function () { ReactDefaultBatchingStrategy.isBatchingUpdates = false; },
}
flushBatchedUpdates:这个函数作用即对组件进行批量更新(虚拟Dom到真实Dom的映射过程),重要的事情再说一遍,这个函数作用即对组件进行批量更新。(这个函数暂不深入研究,主要因为我也没参透这里面具体逻辑,但这不影响我们整体流程介绍,我们需要做的就是记住,在这个函数完成组件们的批量更新行为)。
ReactDefaultBatchingStrategy.isBatchingUpdates:这个变量控制着React是否进行批量更新操作,如果是true,那么遇到更新操作就会进行批量更新,false则不进行批量更新(即发生一次更新,就立即更新)。初始值为false。
现在,我们用一张图看看 React批量更新策略事务 的流程:
了解了React批量更新策略事务,那么这个事务将用在什么地方呢,或者说,React中哪些方法会作为该事务的目标方法,然后启动React批量更新策略事务整个流程呢。让我们继续:
3,合成事件中的setState行为
前面做了这么多铺垫,现在我们就要开始正题,这里以合成事件执行流程举例。
3.1,开启批量更新(在合成事件执行之前)
下面是一个简单的React组件,我们关注点放在click事件(合成事件)中,即从点击add按钮开始说起:
import React from 'react';
import ReactDom from 'react-dom'
class Conuter extends React.Component {
constructor(props) {
super(props)
this.state = {
num: 1
}
}
click = () => {
console.log('this.state.num:', this.state.num);
this.setState({ num: this.state.num + 1 })
this.setState({ num: this.state.num + 2 })
console.log('this.state.num:', this.state.num);
}
render() {
return <div>
<span>{this.state.num}</span>
<button onClick={this.click}>add</button>
</div>
}
}
ReactDom.render(<Conuter />, document.getElementById('root'));
首先,点击click事件,会将React批量更新策略中的批量更新标识设置为true,即开启React批量更新,让我们在React源码中找到这个操作:
// 源码坐标:
// src/renderers/dom/client/ReactEventListener.js
// line:155
dispatchEvent: function(topLevelType, nativeEvent) {
if (!ReactEventListener._enabled) {
return;
}
var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
topLevelType,
nativeEvent,
);
try {
// Event queue being processed in the same cycle allows
// `preventDefault`.
// 将在这里开启React批量更新
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
} finally {
TopLevelCallbackBookKeeping.release(bookKeeping);
}
}
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping):这里会开启React批量更新,即将ReactDefaultBatchingStrategy.isBatchingUpdates设置为true。
进入ReactUpdates.batchedUpdates,看看这里除了开启React批量更新还做了什么:
// 源码坐标:
// src/renderers/shared/stack/reconciler/ReactDefaultBatchingStrategy.js
// line:52
var ReactDefaultBatchingStrategy = {
// 1,isBatchingUpdates:之前一直说的 React批量更新标识 就在这里
isBatchingUpdates: false,
// 2,batchedUpdates:ReactUpdates.batchedUpdates调用的就是这个batchedUpdates函数
batchedUpdates: function(callback, a, b, c, d, e) {
// 3,alreadyBatchingUpdates:缓存当前批量更新标识
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
// 4,将批量更新标识设置true,即之后将开启批量更新
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
// 5,alreadyBatchingUpdates即初始isBatchingUpdates,初始isBatchingUpdates为false,所以这里不会进去
if (alreadyBatchingUpdates) {
return callback(a, b, c, d, e);
} else {
// 6,alreadyBatchingUpdates为false,执行这里代码
return transaction.perform(callback, null, a, b, c, d, e);
}
},
};
1,首先,在代码注释4的位置,将批量更新标识isBatchingUpdates从默认值false,设为true,即开启批量更新,
2,由于之前批量跟新标识为false,所以batchedUpdates(ReactUpdates.batchedUpdates)最后会进入注释6的代码,这段代码中的transaction就是前面介绍的 React批量更新策略事务(ReactDefaultBatchingStrategyTransaction)
3,transaction.perform(callback, null, a, b, c, d, e);这段代码的含义其实就是启动React批量更新策略事务,其中callBack就是事务中的目标方法:
4,所以transaction.perform(callback, null, a, b, c, d, e)执行本质就是:
- 1,执行callback
- 2,执行组件批量更新
- 3,执行批量更新标识重置为false
5,对于callback,可以简单的认为就是click事件,所以现在开始执行click函数。
3.2,执行合成事件
回顾一下click代码:
click = () => {
console.log('this.state.num:', this.state.num); // 1
this.setState({ num: this.state.num + 1 }) // 2
this.setState({ num: this.state.num + 2 }) // 3
console.log('this.state.num:', this.state.num); // 4
}
执行第一行代码,输出this.state.num初始值:1
执行第二行代码:this.setState({ num: this.state.num + 1 })
这里我们终于讲到了本文主人公setState,为了清晰弄清楚setState做了什么,让我们进入setState源码(别怕,简单):
// 源码坐标:
// src/isomorphic/modern/class/ReactBaseClasses.js
// line:60
// partialState即this.setState中第一个参数,callback即this.setState中第二个参数
ReactComponent.prototype.setState = function(partialState, callback) {
//...
// 1,使用updater.enqueueSetState处理设置的state
this.updater.enqueueSetState(this, partialState);
// 2,使用updater.enqueueCallback处理设置的callback
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
this.setState({ num: this.state.num + 1 })中的{ num: this.state.num + 1 }即源码中的partialState,我们继续进入enqueueSetState看看做了什么:
// 源码坐标:
// src/renderers/shared/stack/reconciler/ReactUpdateQueue.js
// line:232
enqueueSetState: function(publicInstance, partialState) {
// ...
// 1,获取当前组件实例的内部实例
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
// ...
// 2,向内部实例中的_pendingStateQueue添加我们的partialState
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
queue.push(partialState);
// 3,将内部实例交给enqueueUpdate执行
enqueueUpdate(internalInstance);
},
结合上面注释,enqueueSetState做的事简而言之:
-
1,将 partialState 暂存入组件内部实例的队列(_pendingStateQueue)中
-
2,将 组件内部实例 交给enqueueUpdate继续处理
-
组件内部实例(internalInstance):这里就认为这是我们当前的组件就好了
进入enqueueUpdate看看这个函数接收到我们的组件内部实例做了什么(别怕,简单):
// 源码坐标:
// src/renderers/shared/stack/reconciler/ReactUpdateQueue.js
// line:213
function enqueueUpdate(component) {
// ...
// 1,如果没有开启批量更新标识,直接对组件进行更新
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
// 2,如果开启批量更新标识,则将组件存入脏组件集合中
dirtyComponents.push(component);
// ...
}
enqueueUpdate做的事很简单,就是将我们的组件放入脏组件集合中(因为在click事件执行之前,前面有说,批量更新标识被设置成true,所以这里走注释2的代码)
至此,this.setState({ num: this.state.num + 1 })执行完毕,可以看到并没有发生组件的更新,做的事仅仅是:
-
1,将 partialState:
{ num: this.state.num + 1 }
保存到队列(_pendingStateQueue)中 -
2,将 当前组件(组件内部实例) 添加到脏组件(dirtyComponents)集合中
现在继续执行click事件中第三行代码:this.setState({ num: this.state.num + 2 })
click = () => {
console.log('this.state.num:', this.state.num); // 1
this.setState({ num: this.state.num + 1 }) // 2
this.setState({ num: this.state.num + 2 }) // 3
console.log('this.state.num:', this.state.num); // 4
}
同第二行代码,第三行代码执行结果也是:
-
1,将 partialState:
{ num: this.state.num + 2 }
保存到队列(_pendingStateQueue)中 -
2,将 当前组件(组件内部实例) 添加到脏组件(dirtyComponents)集合中
最后执行第四行代码,输出this.state.num依然是初始值1,原因很简单:
上面分析了第二行第三行setState代码,执行完成之后并没有做任何组件更新操作(仅保存了将要更新的state与当前组件),所以此时访问this.state获取到的是组件初始值,或者说是组件更新前的值,所以输出this.state.num:1
至此,click事件执行完毕,此时回到React批量更新策略事务(ReactDefaultBatchingStrategyTransaction)中:
此时目标方法click执行完毕,继续执行React批量更新策略事务中第一个close方法 :FLUSH_BATCHED_UPDATES.close,即开始执行组件更新(取出脏组件队列中的脏组件及对应state完成组件更新)
组件更新完毕,执行React批量更新策略事务中第二个close方法 :RESET_BATCHED_UPDATES.close,即将批量更新标识isBatchingUpdates重置为false,即关闭批量更新。
至此,点击add按钮整个流程执行完毕,我们回顾一下整个流程做了什么:
-
1,React批量更新策略的批量更新标识isBatchingUpdates设置为true(默认值false),即开启批量更新
-
2,将合成事件(这里即click事件)添加到React批量更新策略事务运行
-
3,执行合成事件中代码,遇到setState代码则:
-
3.1,将要设置的partialState添加到待定state队列(_pendingStateQueue)中,不执行state更新
-
3.2,将当前组件添加到脏组件集合中
-
-
4,合成事件执行完毕,执行到React批量更新策略事务中首个close方法(FLUSH_BATCHED_UPDATES.close),该方法即对所有脏组件进行组件更新,我们的state也将在这一步完成更新。
-
5,组件更新完毕,执行到React批量更新策略事务中第二个close方法(RESET_BATCHED_UPDATES.close),重置批量更新标识isBatchingUpdates:false,即关闭批量更新。
4,生命周期中的setState行为
前面介绍了合成事件中的setState中的setState行为,其实对于生命周期中setState行为也是如此。让我们从挂载阶段开始说起(ReactDom.render)
React挂载阶段主要源代码:
// 源码坐标:
// src/renderers/dom/client/ReactMount.js
// line:362
_renderNewRootComponent: function(
nextElement,
container,
shouldReuseMarkup,
context,
) {
// ...
// 1,监听窗口滚动和调整大小事件。我们缓存滚动值。应用程序代码可以访问它们而不触发回流
ReactBrowserEventEmitter.ensureScrollValueMonitoring();
// 2,创建组件实例
var componentInstance = instantiateReactComponent(nextElement, false);
// 3,开始批量挂载组件(从根节点开始)
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
componentInstance,
container,
shouldReuseMarkup,
context,
);
//
var wrapperID = componentInstance._instance.rootID;
instancesByReactRootID[wrapperID] = componentInstance;
return componentInstance;
},
-
1,我们可以很清晰的看到,挂载阶段,首先创建了组件树实例,注释1
-
2,之后调用ReactUpdates.batchedUpdates即开启React批量更新事务策略,其中目标方法即batchedMountComponentIntoNode,顾名思义,批量对组件树进行挂载操作,结合前面的React批量更新事务策略具体一点来看:
通过前面合成事件执行流程,很容易看出来,挂载阶段也会开启组件批量更新,如果遇到setState也会暂存partialState以及当前组件,最后对所有脏组件进行批量更新:
-
1,获取根组件实例
-
2,即开启组件批量更新,React批量更新策略的批量更新标识isBatchingUpdates设置为true(默认值false)
-
3,进行组件挂载阶段
-
3,挂载阶段生命周期中遇到setState代码则:
-
3.1,将要设置的partialState添加到待定state队列(_pendingStateQueue)中,不执行state更新
-
3.2,将当前组件添加到脏组件集合中
-
-
4,执行到React批量更新策略事务中首个close方法(FLUSH_BATCHED_UPDATES.close),该方法即对所有脏组件进行组件更新,我们的state也将在这一步完成更新。
-
5,组件树完毕,执行到React批量更新策略事务中第二个close方法(RESET_BATCHED_UPDATES.close),重置批量更新标识isBatchingUpdates:false,即关闭批量更新。
所以对于挂载阶段的生命周期componentWillMount,componentDidMount中的代码全部运行在React批量更新策略事务中,所以这些生命周期中setState也会进行批量更新。
对于更新阶段的大部分生命周期(例如componentWillUpdate)是不可以进行setState操作(出现更新死循环),而卸载阶段生命周期(componentWillUnmount)进行setState无效(组件卸载,setState也将失去意义)。
不过有一个生命周期例外,即componentWillReceiveProps,这个生命周期中可以使用setState,且会进行批量跟新,这块源码没有看,不过猜测应该也是处于React批量更新策略事务中运行,所以可以进行批量更新。
5,关于setState的一些问题的回答
前面我们已经了解了合成事件及组件挂载阶段生命周期中setState的机制,现在让我们在此基础上解决一些setState常见问题
5.1,关于异步的setState
通过前面setState介绍,我们很容易知道setState并非异步,本质还是同步的,只不过它的表现形式是异步的,而且只在合成事件与挂载阶段生命周期(componentWillMount,componentDidMount)以及componentWillReceiveProps才是异步的表现形式
-
setState的异步表现形式主要是在合成事件以及挂载阶段(&componentWillReceiveProps)
-
在这些方法运行之前会打开React批量更新策略中的批量更新标识isBatchingUpdates(默认值false,设置为true),开启批量更新,同时将这些方法添加进React批量更新策略事务中
-
位于React批量更新策略事务中的方法,遇到setState会将setState的参数partialState添加进入对应组件的_pendingStateQueue中,同时组件加入脏组件集合
-
当这些目标方法执行完毕,此时组件(&state)并没有发生更新,但是目标方法内同步代码已经执行完毕,所以访问this.state获取到的仍然是原来的state,即给人一种setState是异步的感觉
-
目标方法执行完毕,会将所有脏组件从根组件开始进行更新(包括state)
-
最后关闭React批量更新标识(isBatchingUpdates重置为fasle)
// this.state.num 初始值 1 // 1,isBatchingUpdates:true // 开启批量更新 click = () => { console.log('this.state.num:', this.state.num); // 输出1 this.setState({ num: this.state.num + 1 }) console.log('this.state.num:', this.state.num); // 输出1 } // 2,isBatchingUpdates:fasle // 关闭批量更新
5.2,关于同步的setState
同步的setState一般存在于异步事件中,比如定时器事件,dom原生事件,Promise微任务,出现这种情况的本质是这些异步事件中的setState并不处于React批量更新策略中,因此setState一次,组件rerender一次
让我们用一个合成事件例子分析同步的setState:
// this.state.num 初始值 1
click = () => { // 1
console.log('this.state.num:', this.state.num); // 2
this.setState({ num: this.state.num + 1 }) // 3
console.log('this.state.num:', this.state.num); // 4
setTimeout(() => { // 5
console.log('this.state.num:', this.state.num); // 6
this.setState({ num: this.state.num + 1 }) // 7
console.log('this.state.num:', this.state.num); // 8
},0) // 9
console.log('this.state.num:', this.state.num); // 10
} // 11
-
注释1:开启React批量更新标识(isBatchingUpdates设为true),将click加入React批量更新策略事务中执行
-
注释2:输出当前this.state.num:1
-
注释3:将partialState:{ num: this.state.num + 1 } 保存到组件的_pendingStateQueue中,组件放入脏组件(dirtyComponent)集合中,并不发生组件更新
-
注释4:组件不发生更新,state不发生更新,输出原this.state.num:1
-
注释5:发现定时器任务setTimeout,交给定时器线程计时,计时结束将注册任务函数加入任务队列(事件循环中的任务队列)
// 定时器注册任务函数 () => { // 5 console.log('this.state.num:', this.state.num); // 6 this.setState({ num: this.state.num + 1 }) // 7 console.log('this.state.num:', this.state.num); // 8 }
-
注释10:组件不发生更新,state不发生更新,输出原this.state.num:1
-
注释11:click事件执行完毕:
-
执行React批量更新策略事务第一个close方法:FLUSH_BATCHED_UPDATES.close,完成组件更新,此时this.state.num被更新为2
-
执行React批量更新策略事务第二个close方法:RESET_BATCHED_UPDATES.close,重置批量更新标识isBatchingUpdates为false,关闭批量更新
-
-
注释5:同步代码执行完毕(此时this.state.num为2,isBatchingUpdates为false),取出任务队列中的定时器注册函数执行
-
注释6:this.state.num在注释11位置更新为2,所以此时输出this.state.num:2
-
注释7:此时批量更新标识(isBatchingUpdates)为false,且没有开启React批量更新策略事务,此时直接执行 this.setState({ num: this.state.num + 1 })。回顾之前setState源码分析部分:
-
1,首先将partialState加入组件的_pendingStateQueue中,之后将组件交给enqueueUpdate处理
// 源码坐标: // src/renderers/shared/stack/reconciler/ReactUpdateQueue.js // line:232 enqueueSetState: function(publicInstance, partialState) { // ... // 1,获取当前组件实例的内部实例 var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState'); // ... // 2,向内部实例中的_pendingStateQueue添加我们的partialState var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); queue.push(partialState); // 3,将内部实例交给enqueueUpdate执行 enqueueUpdate(internalInstance); },
-
2,进入enqueueUpdate:
// 源码坐标: // src/renderers/shared/stack/reconciler/ReactUpdateQueue.js // line:213 function enqueueUpdate(component) { // ... // 1,如果没有开启批量更新标识,直接对组件进行更新 if (!batchingStrategy.isBatchingUpdates) { batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } // 2,如果开启批量更新标识,则将组件存入脏组件集合中 dirtyComponents.push(component); // ... }
-
此时批量更新标识(isBatchingUpdates)为false,所以执行batchingStrategy.batchedUpdates(enqueueUpdate, component);
-
这段代码含义即直接更新组件(&state)
-
-
3,组件更新完毕,state完成更新,此时this.state.num为3
-
-
注释8:this.state.num在注释7位置更新为3,所以此时输出this.state.num:3
所以最后输出结果是:
click = () => { // 1
console.log('this.state.num:', this.state.num); // 2
this.setState({ num: this.state.num + 1 }) // 3
console.log('this.state.num:', this.state.num); // 4
setTimeout(() => { // 5
console.log('this.state.num:', this.state.num); // 6
this.setState({ num: this.state.num + 1 }) // 7
console.log('this.state.num:', this.state.num); // 8
},0) // 9
console.log('this.state.num:', this.state.num); // 10
} // 11
// 输出:
// this.state.num: 1 // from注释2
// this.state.num: 1 // from注释4
// this.state.num: 1 // from注释10
// this.state.num: 2 // from注释6
// this.state.num: 3 // from注释8
5.3 关于多次setState同一个值,只有最后一次setState有效
这中情况只出现在合成事件,挂载阶段(componentWillMount,componentDidMount),componentWillReceivePropse,这些位于React批量更新策略事务中的行为方法。对于异步中的setState则不会有这种行为,因为异步代码不再React批量更新策略事务中,setState一次组件更新一次。
出现这种现象主要是因为在一次批量更新策略事务中,目标方法中的每一次setState的partialState都会添加进入当前组件_pendingStateQueue中,而在脏组件更新阶段会遍历所有的partialState与原state进行Object.assign合并,因此,对于state对象中同名属性,最后一个partialState的该属性会覆盖之前partialState对该属性的设置,因此只有最后一次setState设置的属性才是终值
结合这一部分源代码_processPendingState分析:
// 源码坐标:
// /src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
// line:896
// _processPendingState:将setState的partialState与原state合并
_processPendingState: function(props, context) {
// 1,inst:组件实例
var inst = this._instance;
// 2,queue:_pendingStateQueue
var queue = this._pendingStateQueue;
// ...
// 3,nextState:这里不管replace,nextState即组件实例state对象
var nextState = Object.assign({}, replace ? queue[0] : inst.state);
// 4,遍历_pendingStateQueue中所有partialState,使用 Object.assign 合并到原state内
for (var i = replace ? 1 : 0; i < queue.length; i++) {
var partial = queue[i];
Object.assign( nextState,
typeof partial === 'function'
// 5,如果是函数,获取函数返回值作为partialState合并进原state
? partial.call(inst, nextState, props, context)
// 6,如果是state(一般设置的state对象),则直接向原state合并
: partial,
);
}
//
return nextState;
},
在注释6位置,对于每一个partialState都与原state使用Object.assign进行合并,因此对于同名属性,只有最后一个partialState才会作为该属性终值,像下面这样:
nextState = { a: 1 }
_pendingStateQueue = [{ a: 2 }, { a: 3 }]
nextState = Object.assign(nextState, ..._pendingStateQueue)
// nextState: { a : 3 }
5.4 关于多次setState同一值,如果传入函数形式state,则每一次setState都会生效
这是因为源码_processPendingState处理函数形式的partialState,每次都会将上一次更新后的state值作为函数形式的partialState的第一个参数(如下源码注释5),我们即可在函数形式partialState中获取最新state,随后返回partialState更新后的state通过Object.assign与上一次state值进行合并,完成state更新,所以这就相当于使用函数形式的partialState对state进行迭代更新,保证每一次state都能更新成功。
// 源码坐标:
// /src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
// line:896
// _processPendingState:将setState的partialState与原state合并
_processPendingState: function(props, context) {
// 1,inst:组件实例
var inst = this._instance;
// 2,queue:_pendingStateQueue
var queue = this._pendingStateQueue;
// ...
// 3,nextState:这里不管replace,nextState即组件实例state对象
var nextState = Object.assign({}, replace ? queue[0] : inst.state);
// 4,遍历_pendingStateQueue中所有partialState,使用 Object.assign 合并到原state内
for (var i = replace ? 1 : 0; i < queue.length; i++) {
var partial = queue[i];
Object.assign( nextState,
typeof partial === 'function'
// 5,如果是函数,获取函数返回值作为partialState合并进原state
? partial.call(inst, nextState, props, context)
// 6,如果是state(一般设置的state对象),则直接向原state合并
: partial,
);
}
//
return nextState;
},
6,最后
至此,setState已经介绍完毕,如果有空我会更新一张setState流程图供参阅,当然对于本文也参阅了大量中外文章以及React15源码,欢迎批评指正与交流。