以下的生命周期都是在 React 15 的生命周期, React 16 的生命周期 API 已经发生变化。React 16 加入了 getDerivedStateFromProps
(直译:从 props 中拿到派生 state)和getSnapshotBeforeUpdate
(直译:在更新之前拿到快照),现在生命周期中的 ComponentWillMount
.componentWillReceivedProps
和ComponentWillUpdate
将在 React 17 中废除。
3.3 生命周期的管理艺术
React 组件的生命周期就是一个有限状态机运动的过程。
3.3.1 初探 React 生命周期
组件的生命周期在不同状态下又不同的执行顺序:
在使用 ES6 Class 语法创建组件的时候,static defaultProps = {}
实际上就是在走生命周期的getDefaultProps
,this.setState = {}
就是生命周期的 getInitialState
。
3.3.2 详解 React 生命周期
细节讲解
最外层的元素实际上不是 <App/>,而是在html中的那个真实 DOM节点 <div id="app"></app>
,被称为 TopLevelWrapper,是树的根节点,被赋予的节点ID是1,从他开始,依次标记每个 组件。且只有组件会被标记ID,普通dom标记不会被标上id(尽管在vitrutal dom里普通标签也是 reactElement 类型)。
每一个被mounting的组件都会执行构造函数 ReactCompositeComponentMixin(element)
,每个组件都是由这个构造函数实例出来的对象的一个属性(this._currentElement = element
),这个构造函数实例出来的对象包括以下属性:
this._currentElement: 当前元素
this._rootNodeID: 每个组件都会被编号,从 1开始
this._nativeParent: 组件的父组件
...
与更新有关的
this._pendingElement = null;
this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;
this._renderedNodeType = null;
this._renderedComponent = null;
this._context = null;
this._mountOrder = 0;
this._topLevelWrapper = null;
// See ReactUpdates and ReactUpdateQueue.
this._pendingCallbacks = null;
阶段一: MOUNTING
mountComponent 负责管理生命周期中的 getInitialState
,componentWillMount
,render
,componentDidMount
,而getDefaultProps
实际上是构造函数管理的,所以这就是为什么第二次挂载组件的时候getDefaultProps
不会触发。
首先,通过 mountComponent 挂载组件,初始化序号,标记等参数,判断是否是无状态组件,并进行对应的组件初始化工作,如初始化 props,context等参数。利用 getInitialState
获取初始化 state,初始化更新队列和更新状态。
若存在 componentWillMount
,则执行,在 componentWillMount
中调用 setState()
是不会触发 re-render 的,而是会进行 state 合并。且这个合并(inst.state = this._processPendingState(init.props, inst.context)
)是在componentWillMount
之后才执行。,所以 componentWillMount
中的 this.state
并不是最新的。
因此,React 更新 state 的过程就是:利用更新队列 this._penndingStateQueue
以及更新状态this._pendingReplaceState
和 this._pendingForceUpdate
来实现 state 的异步更新队列。
mountComponent本质上是通过**递归**渲染的。所以父组件的
componentWillMount在子组件的
componentWillMount之前调用,
componentDidMount在子组件的
componentDidMount```之后调用。(合情合理,只有子组件都挂在好了,父组件才算挂载好了)。
实际上,无状态组件也会执行 mountComponent
,只是他们没有生命周期而已
阶段二 RECEIVE_PROPS
updateComponent 负责生命周期中的 componentWillReceiveProps
,shouldComponentUpdate
,componentWillUpdate
,render
和 componentDidUpdate
。
componentWillReceiveProps
只有在组件的父组件更新时才会调用,
阶段三 UNMOUNTING
unmountComponent 负责管理生命周期中的 componentWillUnmount
。
在这个阶段 setState()
是无效的,因为组件的属性(_renderedNodeType
,_renderedComponent
,_instance
,_pendingStateQueue
,_pendingReplaceState
,_pendingForceUpdate
,_pendingCallbacks
,_pendingElement
,_context
,_rootNodeID
,_topLevelWrapper
)都会被置 null,这些是更新队列,更新状态,公共类等(搞清楚这些属性的作用)。
3.3.3 无状态组件
无状态组件的构造函数上就只有一个 render
方法。
3.4 解密 setState 机制
setState 会通过一个更新队列实现 state 更新,将需要更新的 state 合并后放入状态队列。如果直接用 this.state.value=1
的方式修改state,很有可能在更新队列中被其他值覆盖。React 使用 了更新队列合并多次 state 的修改,避免多次更新。
setState
中会调用 _processPendingState
,用于状态合并
// performUpdateIfNecessary 之后调用
var nextState = this._processPendingState(nextProps, nextContext);
// _processPendingState执行 state 的合并
_processPendingState: function (props, context) {
var inst = this._instance;
// queue 就是更新队列,一个数组
var queue = this._pendingStateQueue;
var replace = this._pendingReplaceState;
// 把 _pendingReplaceState和_pendingStateQueue 赋给 queue 和 replace 后马上置空
this._pendingReplaceState = false;
this._pendingStateQueue = null;
// 如果更新队列无数据,直接返回原来的state
if (!queue) {
return inst.state;
}
if (replace && queue.length === 1) {
return queue[0];
}
var nextState = _assign({}, replace ? queue[0] : inst.state);
for (var i = replace ? 1 : 0; i < queue.length; i++) {
var partial = queue[i];
// partial 可能是函数,也可能是对象,看你怎么调的 setState
_assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
}
// nextState 里是新的 state, eg: {a: 1, b: 0}
return nextState;
}
setState 实际上会执行 enqueueSetState
的方法,将partialState
和_pendingStateQueue
合并,并用 enqueueSetState
执行 state 更新。
更新 state 还有一个方法是 enqueueForceUpdate
,它可以不调用 setState
就更新 state,应该是老手使用的。还有一个 enqueueReplaceState
是直接替换 state,但使用它就无法保证 state 的不可变性。而且不会立即更新,所以在调用enqueueReplaceState
后再调用 this.state
有可能拿到的是旧的值。
// 都是调用 warnTDZ,但第二个参数不同
enqueueForceUpdate: function (publicInstance) {
warnTDZ(publicInstance, 'forceUpdate');
}
enqueueReplaceState: function (publicInstance, completeState) {
warnTDZ(publicInstance, 'replaceState');
}
enqueueSetState: function (publicInstance, partialState) {
warnTDZ(publicInstance, 'setState');
}
但是很奇怪warnTDZ
只是用来在development模式下返回一个警告,你调用了一个没有挂载的组件???
3.4.2 setState 循环调用的风险
只要存在 _pendingElement
(待更新Element),_pendingStateQueue
(待更新队列),_pendingForceUpdate
(待强迫更新队列),就会执行 performIfNecessary
,调用方式如下:
如果在shouldComponetUpdate
和componentWillUpdate
中调用 setState
,会让 _pendingStateQueue
不为 null
,performUpdateIfNecessary
就会执行updateComponent
,而updateComponent
又会调用shouldComponetUpdate
和componentWillUpdate
,从而导致 循环调用(试了下真的会,最后导致栈溢出)
彩蛋: 自己发现的 setState 现象
-
setState
不被允许传入null
,传入null
会报错,提醒你可以用forceUpdate
ReactComponent.prototype.setState = function (partialState, callback) {
// 对 partialState 进行校验,必须是 object ,function ,最次是 null
!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null)
? process.env.NODE_ENV !== 'production'
? invariant(false, 'setState(...): takes an object of state variables to update or a ' + 'function which returns an object of state variables.')
: invariant(false)
: void 0;
// 传 null 会有警告,建议你用 forceUpdate
if (process.env.NODE_ENV !== 'production') {
ReactInstrumentation.debugTool.onSetState();
process.env.NODE_ENV !== 'production' ? warning(partialState != null, 'setState(...): You passed an undefined or null state object; ' + 'instead, use forceUpdate().') : void 0;
}
// 进入 enqueueSetState, 传入的 this 是 组件本身
this.updater.enqueueSetState(this, partialState);
// 执行 回调
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
3.4.3 setState 调用栈
可以看到最终 setState
用 enqueueSetState
执行 state 更新,下面将介绍 enqueueSetState
将如何更新
容易混淆的:
_pendingProcessQueue
和_processPendingState
:
_pendingProcessQueue
是更新队列,一个数组,_processPendingState
是一个方法,更新队列在_processPendingState
中被遍历,最后_processPendingState
返回 nextState
是合并了更新数据后的最新 state(包括没有被变动的 state)。