这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。
react 版本:v17.0.3
ClassComponent的三种更新情形
在《React 源码解读之class组件更新updateClassComponent (一)》一文中我们提到,对于 ClassComponent 的更新,有三种情形:
-
情形一:ClassComponent实例未被创建,此时会调用 constructClassInstance 方法构建class组件实例,然后调用 mountClassInstance 方法挂载class组件,并将 shouldUpdate 置为 true,标记组件需要更新渲染。
-
情形二:ClassComponent实例已经存在,但current(当前渲染在界面上的fiber树) 为null,即 ClassComponent 是初次渲染,此时调用 resumeMountClassInstance 方法,复用ClassComponent实例,并更新 state/props,然后会执行 componentWillMount 生命周期函数。
-
情形三:ClassComponent实例已经存在,且已经是多次渲染,此时调用 updateClassInstance 方法执行更新操作,且会执行 componentWillUpdate 生命周期函数。
在本文中,我们将对 ClassComponent 更新的第一种情形进行解读。
// react-reconciler/src/ReactFiberBeginWork.new.js
// 组件实例还未创建
if (instance === null) {
if (current !== null) {
current.alternate = null;
workInProgress.alternate = null;
// Since this is conceptually a new fiber, schedule a Placement effect
workInProgress.flags |= Placement;
}
// 构造 class组件实例
constructClassInstance(workInProgress, Component, nextProps);
// 挂载 class组件
mountClassInstance(workInProgress, Component, nextProps, renderLanes);
// shouldUpdate 用于标记组件是否需要更新
// 由于组件是是首次构建,但还未渲染,因此设置为true,标记组件需要更新渲染
shouldUpdate = true;
}
情形一:ClassComponent实例未被创建
在 ClassComponent 更新的第一种情形中,首先会调用 constructClassInstance 方法构建class组件实例,然后再调用 mountClassInstance 方法挂载class组件。
我们先来看看class组件实例是如何构建出来的。
构建class组件实例 -- constructClassInstance
//react-reconciler/src/ReactFiberClassComponent.new.js
function constructClassInstance(
workInProgress: Fiber,
ctor: any,
props: any,
): any {
let isLegacyContextConsumer = false;
let unmaskedContext = emptyContextObject;
let context = emptyContextObject;
const contextType = ctor.contextType;
// ...
// ctor 即 workInProgress.type,也就是我们开发中定义class组件时的那个 类
let instance = new ctor(props, context);
// ...
// instance.state 是我们在开发中的 class组件中的 this.state
const state = (workInProgress.memoizedState =
instance.state !== null && instance.state !== undefined
? instance.state
: null);
// 初始化 class组件实例,即初始化 workInProgress 和 instance
adoptClassInstance(workInProgress, instance);
//...
return instance;
}
在 constructClassInstance 函数中,主要做了一件事情,那就是实例化我们在开发中定义的类组件,然后调用 adoptClassInstance 方法初始化 class组件实例,把class组件实例添加到 workInProgress 节点上。
adoptClassInstance
// react-reconciler/src/ReactFiberClassComponent.new.js
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
// 将类组件的 updater 对象赋值给实例的 updater
instance.updater = classComponentUpdater;
// 初次渲染或者更新,要把实例挂在fiber节点的stateNode 上
workInProgress.stateNode = instance;
// The instance needs access to the fiber so that it can schedule updates
// 将 fiber 节点挂在 实例的_reactInternals 属性上
setInstance(instance, workInProgress);
if (__DEV__) {
instance._reactInternalInstance = fakeInternalInstance;
}
}
在 adoptClassInstance 函数中,主要做了三件事情:
-
第一件事,将 ClassComponent 的 updater对象 (classComponentUpdater)赋值给 class组件实例的 updater。
// 将类组件的 updater 对象赋值给实例的 updater instance.updater = classComponentUpdater;
-
第二件事,无论组件是初次渲染还是更新,都把class组件实例挂在fiber节点的 stateNode 上。
// 初次渲染或者更新,要把实例挂在fiber节点的stateNode 上 workInProgress.stateNode = instance;
-
第三件事,调用 setInstance 方法,将 workInProgress 挂在class组件实例的 _reactInternals 属性上,这样就可以通过class组件实例找到当前工作的fiber节点workInProgress了。
// 将 fiber 节点挂在 实例的_reactInternals 属性上 setInstance(instance, workInProgress);
setInstance 方法,也就是 ReactInstanceMap.js 文件中的 set 方法:
// shared/ReactInstanceMap.js
export function set(key, value) {
key._reactInternals = value;
}
接下来我们看看这个 classComponentUpdater 对象。
classComponentUpdater
classComponentUpdater 是class组件在初始化的时候拿到的 updater 对象:
const classComponentUpdater = {
// 判断组件的挂载状态
isMounted,
// 对应 setState() 方法 setState 会进行 state 对象合并,只会更新要修改的 key
// inst 即调用 this.setState 时传递进来的this,也就是 class组件实例
// payload 即调用 this.setState 时传递进来的初始state,
// 可以是一个Object对象,也可以是一个function,该function返回一个Object对象
// callback 即调用 this.setState 时传递进来的 回调函数,该回调函数是可选的
// 如果我们想在调用 setState 后立即读取 this.state,就可以使用在 callback 中获取
enqueueSetState(inst, payload, callback) {
// 通过 class组件实例获取 fiber 对象
// this._reactInternals 在 this 上通过 _reactInternals 属性存储fiber对象
const fiber = getInstance(inst);
// 获取当前时间,通过 performance.now() 或 Date.now() 获取的秒数
const eventTime = requestEventTime();
// 创建一个优先级变量(lane模型,通常称为车道模型)
const lane = requestUpdateLane(fiber);
// 创建一个 update 对象
const update = createUpdate(eventTime, lane);
// 将 stateState 传进来的要更新的对象添加到 update 上
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
// 将 setState 的第二个参数 callback 添加到 update 对象上
update.callback = callback;
}
// 将新建的 update 添加到 update链表中
enqueueUpdate(fiber, update, lane);
// 进入任务调度流程
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
if (root !== null) {
entangleTransitions(root, fiber, lane);
}
// ...
if (enableSchedulingProfiler) {
markStateUpdateScheduled(fiber, lane);
}
},
// 对应 replaceState() 方法
enqueueReplaceState(inst, payload, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
const update = createUpdate(eventTime, lane);
update.tag = ReplaceState;
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'replaceState');
}
update.callback = callback;
}
enqueueUpdate(fiber, update, lane);
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
if (root !== null) {
entangleTransitions(root, fiber, lane);
}
// ...
if (enableSchedulingProfiler) {
markStateUpdateScheduled(fiber, lane);
}
},
/**
* enqueueForceUpdate 调用 forceUpdate 时实际调用的更新方法
*/
enqueueForceUpdate(inst, callback) {
// 通过 class组件实例获取 fiber 对象
// this._reactInternals 在 this 上通过 _reactInternals 属性存储fiber对象
const fiber = getInstance(inst);
// 获取当前时间,通过 performance.now() 或 Date.now() 获取的秒数
const eventTime = requestEventTime();
// 创建一个优先级变量(lane模型,通常称为车道模型)
const lane = requestUpdateLane(fiber);
// 创建一个 update 对象
const update = createUpdate(eventTime, lane);
// 与 setState 不同的地方
// update.tag 默认为 0,即更新,将其改成 2 ,即需要强制更新
update.tag = ForceUpdate;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'forceUpdate');
}
// 将 setState 的第二个参数 callback 添加到 update 对象上
update.callback = callback;
}
// 将新建的 update 添加到 update链表中
enqueueUpdate(fiber, update, lane);
// 进入任务调度流程
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
if (root !== null) {
entangleTransitions(root, fiber, lane);
}
// ...
if (enableSchedulingProfiler) {
markForceUpdateScheduled(fiber, lane);
}
},
};
可以看到,classComponentUpdater对象中主要有enqueueSetState、enqueueReplaceState、enqueueForceUpdate三个方法,它们都与触发更新有关:
-
enqueueSetState 方法创建了一个 update.tag 为 UpdateState 的update对象,对应着React的API setState() 方法。
-
enqueueReplaceState 方法创建了一个 update.tag 为 ReplaceState 的update对象,对应着React的API replaceState() 方法。
-
enqueueForceUpdate 方法创建了一个 update.tag 为 ForceUpdate 的update对象,对应着React的API forceUpdate() 方法。
从这三个方法的源码我们可以看出,它们的更新流程是相似的:
-
获取ClassComponent的this对象上的fiber对象
-
计算当前时间和更新优先级
-
根据当前时间和更新优先级创建update对象
-
将setState中要更新的state对象和要执行的callback添加到update对象上
-
将当前update对象加入到updateQueue队列中
-
执行scheduleUpdateOnFiber进入任务调度流程
class组件实例构建完成后,接下来就是在还未渲染的class组件实例上调用生命周期函数了,下面我们来看看 mountClassInstance 函数。
挂载class组件 -- mountClassInstance
// react-reconciler/src/ReactFiberClassComponent.new.js
function mountClassInstance(
workInProgress: Fiber,
ctor: any,
newProps: any,
renderLanes: Lanes,
): void {
// ...
// 在 adoptClassInstance 函数里,将 class组件实例 挂载到了 workInProgress 的 stateNode 属性上
// 因此这里可以通过 workInProgress 的 stateNode 属性获取 class组件实例
const instance = workInProgress.stateNode;
// 更新 props/state
instance.props = newProps;
instance.state = workInProgress.memoizedState;
instance.refs = emptyRefsObject;
// 初始化更新队列
initializeUpdateQueue(workInProgress);
// ctor 即 workInProgress.type,也就是我们开发中定义class组件时的那个 类
const contextType = ctor.contextType;
// 初始化 context
if (typeof contextType === 'object' && contextType !== null) {
instance.context = readContext(contextType);
} else if (disableLegacyContext) {
instance.context = emptyContextObject;
} else {
const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
instance.context = getMaskedContext(workInProgress, unmaskedContext);
}
// ...
// 执行更新 updateQueue
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
// 在 processUpdateQueue 里 更新了 state,所以 instance 的 state 也需要更新
instance.state = workInProgress.memoizedState;
// getDerivedStateFromProps 是 React 新的生命周期函数
// https://zh-hans.reactjs.org/docs/react-component.html#static-getderivedstatefromprops
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
// 执行 getDerivedStateFromProps 生命周期函数,并更新 state
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps,
);
// 更新 instance 的 state
instance.state = workInProgress.memoizedState;
}
// In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for components using the new APIs.
// 判断是否要调用 componentWillMount
// 如果是第一次渲染,是要执行 componentWillMount 的
if (
typeof ctor.getDerivedStateFromProps !== 'function' &&
typeof instance.getSnapshotBeforeUpdate !== 'function' &&
(typeof instance.UNSAFE_componentWillMount === 'function' ||
typeof instance.componentWillMount === 'function')
) {
// 执行 componentWillMount 生命周期函数
callComponentWillMount(workInProgress, instance);
// If we had additional state updates during this life-cycle, let's
// process them now.
// 在 componentWillMount 钩子函数中是有可能执行 setState 的,
// 所以要执行更新 updateQueue,并更新 state
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
// 在 processUpdateQueue 中更新了 state,因此 instance 的 state 也要更新
instance.state = workInProgress.memoizedState;
}
// 添加标记,等到组件真正渲染到 DOM 上的时候,再去调用 componentDidMount 钩子函数
if (typeof instance.componentDidMount === 'function') {
let fiberFlags: Flags = Update;
if (enableSuspenseLayoutEffectSemantics) {
fiberFlags |= LayoutStatic;
}
// ...
workInProgress.flags |= fiberFlags;
}
}
在 mountClassInstance 函数中,做了以下事情:
1、首先通过stateNode属性从workInProgress上获取class组件实例,并更新组件实例上的 state 和 props:
// 在 adoptClassInstance 函数里,将 class组件实例 挂载到了 workInProgress 的 stateNode 属性上
// 因此这里可以通过 workInProgress 的 stateNode 属性获取 class组件实例
const instance = workInProgress.stateNode;
// 更新 props/state
instance.props = newProps;
instance.state = workInProgress.memoizedState;
instance.refs = emptyRefsObject;
2、接着对 updateQueue 进行初始化,将updateQueue挂在workInProgress上:
// 初始化更新队列
initializeUpdateQueue(workInProgress);
3、接下来初始化 context,如果开发中定义class组件时提供了 contextType,则初始化为定义class组件时提供的 context,否则就初始化为一个空的emptyContextObject。
// ctor 即 workInProgress.type,也就是我们开发中定义class组件时的那个 类
const contextType = ctor.contextType;
// 初始化 context
if (typeof contextType === 'object' && contextType !== null) {
instance.context = readContext(contextType);
} else if (disableLegacyContext) {
instance.context = emptyContextObject;
} else {
const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
instance.context = getMaskedContext(workInProgress, unmaskedContext);
}
4、再接着执行processUpdateQueue方法,更新updateQueue,并更新 state。由于在 processUpdateQueue 里更新了 state,因此 class组件实例的 state 也需要更新。
// 执行更新 updateQueue
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
// 在 processUpdateQueue 里 更新了 state,所以 instance 的 state 也需要更新
instance.state = workInProgress.memoizedState;
5、然后判断开发代码中是否提供了 getDerivedStateFromProps 生命周期函数,如果有,则调用 applyDerivedStateFromProps 函数来执行 getDerivedStateFromProps 生命周期函数,并更新 state,并同时更新class组件实例的state
// getDerivedStateFromProps 是 React 新的生命周期函数
// https://zh-hans.reactjs.org/docs/react-component.html#static-getderivedstatefromprops
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
// 执行 getDerivedStateFromProps 生命周期函数,并更新 state
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps,
);
// 更新 instance 的 state
instance.state = workInProgress.memoizedState;
}
6、再然后判断开发代码中是否提供了 componentWillMount 生命周期函数,如果有,则调用 callComponentWillMount 函数来执行 componentWillMount 生命周期函数,并更新 state,同时也更新class组件实例上的 state。在 componentWillMount 生命周期函数中是有可能调用 setState 方法的,因此需要执行processUpdateQueue方法来更新updateQueue,并更新 state,由于在 processUpdateQueue 里更新了 state,因此 class组件实例的 state 也需要更新。
// 判断是否要调用 componentWillMount
// 如果是第一次渲染,是要执行 componentWillMount 的
if (
typeof ctor.getDerivedStateFromProps !== 'function' &&
typeof instance.getSnapshotBeforeUpdate !== 'function' &&
(typeof instance.UNSAFE_componentWillMount === 'function' ||
typeof instance.componentWillMount === 'function')
) {
// 执行 componentWillMount 生命周期函数
callComponentWillMount(workInProgress, instance);
// If we had additional state updates during this life-cycle, let's
// process them now.
// 在 componentWillMount 钩子函数中是有可能执行 setState 的,
// 所以要执行更新 updateQueue,并更新 state
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
// 在 processUpdateQueue 中更新了 state,因此 instance 的 state 也要更新
instance.state = workInProgress.memoizedState;
}
7、最后给 workInProgress 添加类型为 Update 的flags标记,在等到组件真正渲染到 DOM 上的时候,执行 componentDidMount 生命周期函数完成组件的挂载。
// 添加标记,等到组件真正渲染到 DOM 上的时候,再去调用 componentDidMount 钩子函数
if (typeof instance.componentDidMount === 'function') {
let fiberFlags: Flags = Update;
if (enableSuspenseLayoutEffectSemantics) {
fiberFlags |= LayoutStatic;
}
// ...
workInProgress.flags |= fiberFlags;
}
接下来,我们来逐个看看在 mountClassInstance 中调用的函数。
initializeUpdateQueue
initializeUpdateQueue 用于初始化 updateQueue
// react-reconciler/src/ReactUpdateQueue.new.js
export function initializeUpdateQueue<State>(fiber: Fiber): void {
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null,
interleaved: null,
lanes: NoLanes,
},
effects: null,
};
fiber.updateQueue = queue;
}
可以看到,initializeUpdateQueue 函数定义了一个 queue 对象,并将其赋值给了当前工作节点 fiber 的 updateQueue 属性,也就是初始化当前工作节点 workInProgress 的 updateQueue。
processUpdateQueue
processUpdateQueue用于更新 updateQueue 队列,并更新 state
// react-reconciler/src/ReactUpdateQueue.new.js
export function processUpdateQueue<State>(
workInProgress: Fiber,
props: any,
instance: any,
renderLanes: Lanes,
): void {
// This is always non-null on a ClassComponent or HostRoot
// 在 initializeUpdateQueue函数中 初始化更新队列时将 updateQueue 挂载到了 workInProgress 上
// 因此这里可以通过 workInProgress 获取更新队列
const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
// 是否要强制更新,在这里不需要强制更新
hasForceUpdate = false;
if (__DEV__) {
currentlyProcessingQueue = queue.shared;
}
// 更新队列中的第一个 update
let firstBaseUpdate = queue.firstBaseUpdate;
// 更新队列中的最后一个 update
let lastBaseUpdate = queue.lastBaseUpdate;
// Check if there are pending updates. If so, transfer them to the base queue.
// 检查更新队列中是否有待处理的更新,如果有,将它们添加到基本队列中
let pendingQueue = queue.shared.pending;
if (pendingQueue !== null) {
queue.shared.pending = null;
// The pending queue is circular. Disconnect the pointer between first
// and last so that it's non-circular.
const lastPendingUpdate = pendingQueue;
const firstPendingUpdate = lastPendingUpdate.next;
lastPendingUpdate.next = null;
// Append pending updates to base queue
// 将待处理的更新添加到基础队列中
if (lastBaseUpdate === null) {
// 将待更新的第一个 update 对象赋值给更新队列中的第一个更新元素
firstBaseUpdate = firstPendingUpdate;
} else {
// 更新队列中存在要更新的元素,将待更新队列中的第一个更新元素添加到更新队列的末尾,形成链表
lastBaseUpdate.next = firstPendingUpdate;
}
lastBaseUpdate = lastPendingUpdate;
// If there's a current queue, and it's different from the base queue, then
// we need to transfer the updates to that queue, too. Because the base
// queue is a singly-linked list with no cycles, we can append to both
// lists and take advantage of structural sharing.
// TODO: Pass `current` as argument
// 更新当前渲染在界面上的fiber树上的 updateQueue
const current = workInProgress.alternate;
if (current !== null) {
// This is always non-null on a ClassComponent or HostRoot
const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
if (currentLastBaseUpdate !== lastBaseUpdate) {
if (currentLastBaseUpdate === null) {
currentQueue.firstBaseUpdate = firstPendingUpdate;
} else {
currentLastBaseUpdate.next = firstPendingUpdate;
}
currentQueue.lastBaseUpdate = lastPendingUpdate;
}
}
}
// These values may change as we process the queue.
// 当执行更新队列的时候,这些属性可能会被更改
if (firstBaseUpdate !== null) {
// Iterate through the list of updates to compute the result.
// 创建副本变量
let newState = queue.baseState;
// TODO: Don't need to accumulate this. Instead, we can remove renderLanes
// from the original lanes.
let newLanes = NoLanes;
let newBaseState = null;
let newFirstBaseUpdate = null;
let newLastBaseUpdate = null;
let update = firstBaseUpdate;
do {
// 获取 update 的优先级,判断是否需要执行更新
const updateLane = update.lane;
const updateEventTime = update.eventTime;
// 更新队列的第一个 update 的优先级低于 renderLanes
if (!isSubsetOfLanes(renderLanes, updateLane)) {
// Priority is insufficient. Skip this update. If this is the first
// skipped update, the previous update/state is the new base
// update/state.
// 由于优先级低于 renderLanes,不执行该 update 的更新
const clone: Update<State> = {
eventTime: updateEventTime,
lane: updateLane,
tag: update.tag,
payload: update.payload,
callback: update.callback,
next: null,
};
// 本次没有更新的 update,会优先放到下一次去判断要不要更新
if (newLastBaseUpdate === null) {
newFirstBaseUpdate = newLastBaseUpdate = clone;
newBaseState = newState;
} else {
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
// Update the remaining priority in the queue.
newLanes = mergeLanes(newLanes, updateLane);
} else {
// This update does have sufficient priority.
// 该 update 优先级高于 renderLanes,执行更新
if (newLastBaseUpdate !== null) {
const clone: Update<State> = {
eventTime: updateEventTime,
// This update is going to be committed so we never want uncommit
// it. Using NoLane works because 0 is a subset of all bitmasks, so
// this will never be skipped by the check above.
lane: NoLane,
tag: update.tag,
payload: update.payload,
callback: update.callback,
next: null,
};
// 拷贝一份 update
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
// Process this update.
// 执行 update,获取最新的 state
newState = getStateFromUpdate(
workInProgress,
queue,
update,
newState,
props,
instance,
);
// callback 为 this.setState({}, () => {}) 中的回调函数 () => {}
const callback = update.callback;
// setState 执行完后,执行 callback
if (
callback !== null &&
// If the update was already committed, we should not queue its
// callback again.
update.lane !== NoLane
) {
workInProgress.flags |= Callback;
const effects = queue.effects;
if (effects === null) {
queue.effects = [update];
} else {
effects.push(update);
}
}
}
// 执行下一个更新,循环
update = update.next;
if (update === null) {
pendingQueue = queue.shared.pending;
if (pendingQueue === null) {
break;
} else {
// An update was scheduled from inside a reducer. Add the new
// pending updates to the end of the list and keep processing.
const lastPendingUpdate = pendingQueue;
// Intentionally unsound. Pending updates form a circular list, but we
// unravel them when transferring them to the base queue.
const firstPendingUpdate = ((lastPendingUpdate.next: any): Update<State>);
lastPendingUpdate.next = null;
update = firstPendingUpdate;
queue.lastBaseUpdate = lastPendingUpdate;
queue.shared.pending = null;
}
}
} while (true);
// 执行 update 后,更新 queue 上的相关属性
if (newLastBaseUpdate === null) {
newBaseState = newState;
}
queue.baseState = ((newBaseState: any): State);
queue.firstBaseUpdate = newFirstBaseUpdate;
queue.lastBaseUpdate = newLastBaseUpdate;
// Interleaved updates are stored on a separate queue. We aren't going to
// process them during this render, but we do need to track which lanes
// are remaining.
const lastInterleaved = queue.shared.interleaved;
if (lastInterleaved !== null) {
let interleaved = lastInterleaved;
do {
newLanes = mergeLanes(newLanes, interleaved.lane);
interleaved = ((interleaved: any).next: Update<State>);
} while (interleaved !== lastInterleaved);
} else if (firstBaseUpdate === null) {
// `queue.lanes` is used for entangling transitions. We can set it back to
// zero once the queue is empty.
queue.shared.lanes = NoLanes;
}
// Set the remaining expiration time to be whatever is remaining in the queue.
// This should be fine because the only two other things that contribute to
// expiration time are props and context. We're already in the middle of the
// begin phase by the time we start processing the queue, so we've already
// dealt with the props. Context in components that specify
// shouldComponentUpdate is tricky; but we'll have to account for
// that regardless.
markSkippedUpdateLanes(newLanes);
workInProgress.lanes = newLanes;
workInProgress.memoizedState = newState;
}
if (__DEV__) {
currentlyProcessingQueue = null;
}
}
在 processUpdateQueue 函数中:
1、从workInProgress上获取updateQueue,取出updateQueue中的第一个update,然后检查updateQueue中是否有待处理的更新,如果有,就将它们添加到基本队列中,并同时更新当前渲染到屏幕上的fiber树current的updateQueue。
// 因此这里可以通过 workInProgress 获取更新队列
const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
// 是否要强制更新,在这里不需要强制更新
hasForceUpdate = false;
// ...
// 更新队列中的第一个 update
let firstBaseUpdate = queue.firstBaseUpdate;
// 更新队列中的最后一个 update
let lastBaseUpdate = queue.lastBaseUpdate;
// Check if there are pending updates. If so, transfer them to the base queue.
// 检查更新队列中是否有待处理的更新,如果有,将它们添加到基本队列中
let pendingQueue = queue.shared.pending;
if (pendingQueue !== null) {
queue.shared.pending = null;
// The pending queue is circular. Disconnect the pointer between first
// and last so that it's non-circular.
const lastPendingUpdate = pendingQueue;
const firstPendingUpdate = lastPendingUpdate.next;
lastPendingUpdate.next = null;
// Append pending updates to base queue
// 将待处理的更新添加到基础队列中
if (lastBaseUpdate === null) {
// 将待更新的第一个 update 对象赋值给更新队列中的第一个更新元素
firstBaseUpdate = firstPendingUpdate;
} else {
// 更新队列中存在要更新的元素,将待更新队列中的第一个更新元素添加到更新队列的末尾,形成链表
lastBaseUpdate.next = firstPendingUpdate;
}
lastBaseUpdate = lastPendingUpdate;
// 更新当前渲染在界面上的fiber树上的 updateQueue
const current = workInProgress.alternate;
if (current !== null) {
// This is always non-null on a ClassComponent or HostRoot
const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
if (currentLastBaseUpdate !== lastBaseUpdate) {
if (currentLastBaseUpdate === null) {
currentQueue.firstBaseUpdate = firstPendingUpdate;
} else {
currentLastBaseUpdate.next = firstPendingUpdate;
}
currentQueue.lastBaseUpdate = lastPendingUpdate;
}
}
}
2、进入update的循环更新,并根据当前update的优先级,判断是否对当前update执行更新。
3、如果当前update的优先级低于 renderLanes,则不执行当前update的更新。拷贝当前update,将其重新放入updateQueue中,并更新它的更新优先级,等待下一次判断是否需要执行更新。
// 更新队列的第一个 update 的优先级低于 renderLanes
if (!isSubsetOfLanes(renderLanes, updateLane)) {
// Priority is insufficient. Skip this update. If this is the first
// skipped update, the previous update/state is the new base
// update/state.
// 由于优先级低于 renderLanes,不执行该 update 的更新
const clone: Update<State> = {
eventTime: updateEventTime,
lane: updateLane,
tag: update.tag,
payload: update.payload,
callback: update.callback,
next: null,
};
// 本次没有更新的 update,会优先放到下一次去判断要不要更新
if (newLastBaseUpdate === null) {
newFirstBaseUpdate = newLastBaseUpdate = clone;
newBaseState = newState;
} else {
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
// Update the remaining priority in the queue.
newLanes = mergeLanes(newLanes, updateLane);
}
4、如果当前update的优先级高于 renderLanes,则执行当前update的更新,执行 getStateFromUpdate() 获取更新的 state 后,如果该update上有callback的话,即this.setState({}, () => {}) 中的回调函数,设置 workInProgress 的 flags 为 Callback 来“提醒”更新 state 后再执行 callback。
else {
// This update does have sufficient priority.
// 该 update 优先级高于 renderLanes,执行更新
if (newLastBaseUpdate !== null) {
const clone: Update<State> = {
eventTime: updateEventTime,
// This update is going to be committed so we never want uncommit
// it. Using NoLane works because 0 is a subset of all bitmasks, so
// this will never be skipped by the check above.
lane: NoLane,
tag: update.tag,
payload: update.payload,
callback: update.callback,
next: null,
};
// 拷贝一份 update
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
// Process this update.
// 执行 update,获取最新的 state
newState = getStateFromUpdate(
workInProgress,
queue,
update,
newState,
props,
instance,
);
// callback 为 this.setState({}, () => {}) 中的回调函数 () => {}
const callback = update.callback;
// setState 执行完后,执行 callback
if (
callback !== null &&
// If the update was already committed, we should not queue its
// callback again.
update.lane !== NoLane
) {
workInProgress.flags |= Callback;
const effects = queue.effects;
if (effects === null) {
queue.effects = [update];
} else {
effects.push(update);
}
}
}
5、第一个 update 执行完更新后,通过 update = update.next
获取到下一个 update,重复 2、3、4,继续执行下一个update的更新。
// 执行下一个更新,循环
update = update.next;
6、当updateQueue中的所有update都执行完更新后,更新updateQueue和workInProgress上的一些属性。
// 执行 update 后,更新 queue 上的相关属性
if (newLastBaseUpdate === null) {
newBaseState = newState;
}
queue.baseState = ((newBaseState: any): State);
queue.firstBaseUpdate = newFirstBaseUpdate;
queue.lastBaseUpdate = newLastBaseUpdate;
const lastInterleaved = queue.shared.interleaved;
if (lastInterleaved !== null) {
let interleaved = lastInterleaved;
do {
newLanes = mergeLanes(newLanes, interleaved.lane);
interleaved = ((interleaved: any).next: Update<State>);
} while (interleaved !== lastInterleaved);
} else if (firstBaseUpdate === null) {
queue.shared.lanes = NoLanes;
}
markSkippedUpdateLanes(newLanes);
workInProgress.lanes = newLanes;
workInProgress.memoizedState = newState;
getStateFromUpdate
getStateFromUpdate 函数用于获取最新是state
// react-reconciler/src/ReactUpdateQueue.new.js
function getStateFromUpdate<State>(
workInProgress: Fiber,
queue: UpdateQueue<State>,
update: Update<State>,
prevState: State,
nextProps: any,
instance: any,
): any {
switch (update.tag) {
// 通过 replaceState()的更新 即替换更新
// 返回 执行 payload 后的 state
case ReplaceState: {
const payload = update.payload;
if (typeof payload === 'function') {
// Updater function
// 此处删除了 dev 代码
const nextState = payload.call(instance, prevState, nextProps);
// 此处删除了 dev 代码
return nextState;
}
// State object
return payload;
}
// 捕获性更新
case CaptureUpdate: {
workInProgress.flags =
// ~ 的意思是获取除了 ShouldCapture 以外的所有属性,也就是获取了 DidCapture
(workInProgress.flags & ~ShouldCapture) | DidCapture;
}
// Intentional fallthrough
// 通过 setState 更新
case UpdateState: {
const payload = update.payload;
let partialState;
// 如果payload 是 function,则获取执行 payload 后得到的 state
if (typeof payload === 'function') {
// Updater function
// 此处删除了 dev 代码
partialState = payload.call(instance, prevState, nextProps);
// 此处删除了 dev 代码
} else {
// Partial state object
// payload 不是 function,直接将其赋值给 partialState
partialState = payload;
}
// partialState 没有值,则视为没有更新 state,直接返回 prevState
if (partialState === null || partialState === undefined) {
// Null and undefined are treated as no-ops.
return prevState;
}
// Merge the partial state and the previous state.
// 合并 state,并返回合并后的 state
return Object.assign({}, prevState, partialState);
}
// 强制更新
case ForceUpdate: {
hasForceUpdate = true;
return prevState;
}
}
return prevState;
}
getStateFromUpdate 对替换更新(ReplaceState)、捕获性更新(CaptureUpdate)、setState更新(UpdateState)、强制更新(ForceUpdate) 四种更新类型做了不同的处理。我们重点来看看 UpdateState 的情况,也就是我们在开发的时候使用this.setState({xxx})
的情况,如果有新 state 的话,则执行Object.assign({}, prevState, partialState)
来进行 state 的合并,然后返回合并后的 state 。
applyDerivedStateFromProps
applyDerivedStateFromProps 的作用是 执行 getDerivedStateFromProps 生命周期函数,并更新 state
// react-reconciler/src/ReactFiberClassComponent.new.js
function applyDerivedStateFromProps(
workInProgress: Fiber,
ctor: any,
getDerivedStateFromProps: (props: any, state: any) => any,
nextProps: any,
) {
const prevState = workInProgress.memoizedState;
// 执行 getDerivedStateFromProps 生命周期函数
let partialState = getDerivedStateFromProps(nextProps, prevState);
// ...
// Merge the partial state and the previous state.
// 合并新的state和旧的state
const memoizedState =
partialState === null || partialState === undefined
? prevState
: Object.assign({}, prevState, partialState);
// 更新state
workInProgress.memoizedState = memoizedState;
// Once the update queue is empty, persist the derived state onto the
// base state.
// 更新 updateQueue上的 state
if (workInProgress.lanes === NoLanes) {
// Queue is always non-null for classes
const updateQueue: UpdateQueue<any> = (workInProgress.updateQueue: any);
updateQueue.baseState = memoizedState;
}
}
可以看到,在 applyDerivedStateFromProps 函数中, 执行 getDerivedStateFromProps 生命周期函数获取新的 state,然后将新的state和旧的state进行合并,将合并后的state更新到workInProgress上和updateQueue上。
callComponentWillMount
callComponentWillMount的作用就是,执行 componentWillMount 生命周期函数
// react-reconciler/src/ReactFiberClassComponent.new.js
function callComponentWillMount(workInProgress, instance) {
const oldState = instance.state;
// 执行 componentWillMount 生命周期函数
if (typeof instance.componentWillMount === 'function') {
instance.componentWillMount();
}
// 执行 UNSAFE_componentWillMount 生命周期函数
if (typeof instance.UNSAFE_componentWillMount === 'function') {
instance.UNSAFE_componentWillMount();
}
if (oldState !== instance.state) {
if (__DEV__) {
console.error(
'%s.componentWillMount(): Assigning directly to this.state is ' +
"deprecated (except inside a component's " +
'constructor). Use setState instead.',
getComponentNameFromFiber(workInProgress) || 'Component',
);
}
classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
}
}
可以看到,在 callComponentWillMount 函数中,分别执行了 componentWillMount 生命周期函数和 UNSAFE_componentWillMount 生命周期函数。如果是在开发环境,则执行 enqueueReplaceState 来替换更新组件实例上的 state 。
流程图
下面是class组件实例未被创建时,构建class组件实例并挂载组件及执行一些相关生命周期函数的流程图:
总结
本文是 React 源码解读之class组件更新updateClassComponent 系列的第二篇,介绍了ClassComponent的三种更新情形中的第一种。ClassComponent实例未被创建,此时会调用 constructClassInstance 方法构建class组件实例,然后调用 mountClassInstance 方法挂载class组件,并将 shouldUpdate 置为 true,标记组件需要更新渲染。
下一篇文章《React 源码解读之class组件更新updateClassComponent (三)》将介绍ClassComponent的三种更新情形中的第二种。欢迎前往阅读。