updateClassComponent
- instance 不存在,current存在就断开alternate指针,将组件类实例化并挂载
- instance存在,current存在进行实例更新处理
function updateClassComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
nextProps,
renderExpirationTime: ExpirationTime,
) {
let hasContext;
if (isLegacyContextProvider(Component)) {
hasContext = true;
pushLegacyContextProvider(workInProgress);
} else {
hasContext = false;
}
prepareToReadContext(workInProgress, renderExpirationTime);
const instance = workInProgress.stateNode;
let shouldUpdate;
//实例是否存在
if (instance === null) {
//current存在,断开alertnate指针
if (current !== null) {
// An class component without an instance only mounts if it suspended
// inside a non- concurrent tree, in an inconsistent state. We want to
// tree it like a new mount, even though an empty version of it already
// committed. Disconnect the alternate pointers.
current.alternate = null;
workInProgress.alternate = null;
// Since this is conceptually a new fiber, schedule a Placement effect
workInProgress.effectTag |= Placement;
}
// In the initial pass we might need to construct the instance.
// 调用组件的构造器,进行实例化
constructClassInstance(
workInProgress,
Component,
nextProps,
renderExpirationTime,
);
//挂载实例
mountClassInstance(
workInProgress,
Component,
nextProps,
renderExpirationTime,
);
shouldUpdate = true;
} else if (current === null) {
// In a resume, we'll already have an instance we can reuse.
shouldUpdate = resumeMountClassInstance(
workInProgress,
Component,
nextProps,
renderExpirationTime,
);
} else {
//存在current
shouldUpdate = updateClassInstance(
current,
workInProgress,
Component,
nextProps,
renderExpirationTime,
);
}
const nextUnitOfWork = finishClassComponent(
current,
workInProgress,
Component,
shouldUpdate,
hasContext,
renderExpirationTime,
);
return nextUnitOfWork;
}
constructClassInstance
- 对context进行处理,读取context
new ctor(props, context)
构造实例- 给ClassComponent 的实例挂载一个updater对象
- 给instace属性_reactInternalFiber上挂载fiber对象
function constructClassInstance(
workInProgress: Fiber,
ctor: any,
props: any,
renderExpirationTime: ExpirationTime,
): any {
let isLegacyContextConsumer = false;
let unmaskedContext = emptyContextObject;
let context = null;
const contextType = ctor.contextType;
if (typeof contextType === 'object' && contextType !== null) {
context = readContext((contextType: any));
} else {
unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
const contextTypes = ctor.contextTypes;
isLegacyContextConsumer =
contextTypes !== null && contextTypes !== undefined;
context = isLegacyContextConsumer
? getMaskedContext(workInProgress, unmaskedContext)
: emptyContextObject;
}
const instance = new ctor(props, context);
const state = (workInProgress.memoizedState =
instance.state !== null && instance.state !== undefined
? instance.state
: null);
adoptClassInstance(workInProgress, instance);
// Cache unmasked context so we can avoid recreating masked context unless necessary.
// ReactFiberContext usually updates this cache but can't for newly-created instances.
if (isLegacyContextConsumer) {
cacheContext(workInProgress, unmaskedContext, context);
}
return instance;
}
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
instance.updater = classComponentUpdater;
workInProgress.stateNode = instance;
// The instance needs access to the fiber so that it can schedule updates
setInstance(instance, workInProgress);
}
//setInstance就是set 方法。
export function set(key, value) {
key._reactInternalFiber = value;
}
mountClassInstance
- 处理updateQueue,有则计算更新
- 有getDerivedStateFromProps方法就调用之
- 判断是否有componentDidMount 有就修改effectTag
function mountClassInstance(
workInProgress: Fiber,
ctor: any,
newProps: any,
renderExpirationTime: ExpirationTime,
): void {
const instance = workInProgress.stateNode;
instance.props = newProps;
instance.state = workInProgress.memoizedState;
instance.refs = emptyRefsObject;
const contextType = ctor.contextType;
if (typeof contextType === 'object' && contextType !== null) {
instance.context = readContext(contextType);
} else {
const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
instance.context = getMaskedContext(workInProgress, unmaskedContext);
}
let updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
processUpdateQueue(
workInProgress,
updateQueue,
newProps,
instance,
renderExpirationTime,
);
instance.state = workInProgress.memoizedState;
}
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps,
);
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.
if (
typeof ctor.getDerivedStateFromProps !== 'function' &&
typeof instance.getSnapshotBeforeUpdate !== 'function' &&
(typeof instance.UNSAFE_componentWillMount === 'function' ||
typeof instance.componentWillMount === 'function')
) {
callComponentWillMount(workInProgress, instance);
// If we had additional state updates during this life-cycle, let's
// process them now.
updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
processUpdateQueue(
workInProgress,
updateQueue,
newProps,
instance,
renderExpirationTime,
);
instance.state = workInProgress.memoizedState;
}
}
if (typeof instance.componentDidMount === 'function') {
workInProgress.effectTag |= Update;
}
}
export function applyDerivedStateFromProps(
workInProgress: Fiber,
ctor: any,
getDerivedStateFromProps: (props: any, state: any) => any,
nextProps: any,
) {
const prevState = workInProgress.memoizedState;
const partialState = getDerivedStateFromProps(nextProps, prevState);
// Merge the partial state and the previous state.
const memoizedState =
partialState === null || partialState === undefined
? prevState
: Object.assign({}, prevState, partialState);
workInProgress.memoizedState = memoizedState;
// Once the update queue is empty, persist the derived state onto the
// base state.
const updateQueue = workInProgress.updateQueue;
if (updateQueue !== null && workInProgress.expirationTime === NoWork) {
updateQueue.baseState = memoizedState;
}
}
finishClassComponent
- 加上ref的effectTag
- 判断shouldUpdate,为false,就bailout
- 调用instance.render方法返回nextChildren
- 调用reconcileChildren
- 将instance的state赋值workInProgress的memoizedState
function finishClassComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
shouldUpdate: boolean,
hasContext: boolean,
renderExpirationTime: ExpirationTime,
) {
// Refs should update even if shouldComponentUpdate returns false
markRef(current, workInProgress);
const didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect;
if (!shouldUpdate && !didCaptureError) {
// Context providers should defer to sCU for rendering
if (hasContext) {
invalidateContextProvider(workInProgress, Component, false);
}
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
);
}
const instance = workInProgress.stateNode;
// Rerender
ReactCurrentOwner.current = workInProgress;
let nextChildren;
if (
didCaptureError &&
typeof Component.getDerivedStateFromError !== 'function'
) {
// If we captured an error, but getDerivedStateFrom catch is not defined,
// unmount all the children. componentDidCatch will schedule an update to
// re-render a fallback. This is temporary until we migrate everyone to
// the new API.
// TODO: Warn in a future release.
nextChildren = null;
if (enableProfilerTimer) {
stopProfilerTimerIfRunning(workInProgress);
}
} else {
nextChildren = instance.render();
}
// React DevTools reads this flag.
workInProgress.effectTag |= PerformedWork;
if (current !== null && didCaptureError) {
// If we're recovering from an error, reconcile without reusing any of
// the existing children. Conceptually, the normal children and the children
// that are shown on error are two different sets, so we shouldn't reuse
// normal children even if their identities match.
forceUnmountCurrentAndReconcile(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
} else {
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
}
// Memoize state using the values we just used to render.
// TODO: Restructure so we never read values from the instance.
workInProgress.memoizedState = instance.state;
// The context might have changed so we need to recalculate it.
if (hasContext) {
invalidateContextProvider(workInProgress, Component, true);
}
return workInProgress.child;
}
updateClassInstance
- 读取context
- 调用getDerivedStateFromProps
- 调用更新阶段生命周期
处理更新,检查是否应该更新
function updateClassInstance( current: Fiber, workInProgress: Fiber, ctor: any, newProps: any, renderExpirationTime: ExpirationTime, ): boolean { const instance = workInProgress.stateNode; const oldProps = workInProgress.memoizedProps; instance.props = workInProgress.type === workInProgress.elementType ? oldProps : resolveDefaultProps(workInProgress.type, oldProps); const oldContext = instance.context; const contextType = ctor.contextType; let nextContext; if (typeof contextType === 'object' && contextType !== null) { nextContext = readContext(contextType); } else { const nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true); nextContext = getMaskedContext(workInProgress, nextUnmaskedContext); } const getDerivedStateFromProps = ctor.getDerivedStateFromProps; const hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function'; // Note: During these life-cycles, instance.props/instance.state are what // ever the previously attempted to render - not the "current". However, // during componentDidUpdate we pass the "current" props. // In order to support react-lifecycles-compat polyfilled components, // Unsafe lifecycles should not be invoked for components using the new APIs. if ( !hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function') ) { if (oldProps !== newProps || oldContext !== nextContext) { callComponentWillReceiveProps( workInProgress, instance, newProps, nextContext, ); } } resetHasForceUpdateBeforeProcessing(); const oldState = workInProgress.memoizedState; let newState = (instance.state = oldState); let updateQueue = workInProgress.updateQueue; if (updateQueue !== null) { processUpdateQueue( workInProgress, updateQueue, newProps, instance, renderExpirationTime, ); newState = workInProgress.memoizedState; } if ( oldProps === newProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing() ) { // If an update was already in progress, we should schedule an Update // effect even though we're bailing out, so that cWU/cDU are called. if (typeof instance.componentDidUpdate === 'function') { if ( oldProps !== current.memoizedProps || oldState !== current.memoizedState ) { workInProgress.effectTag |= Update; } } if (typeof instance.getSnapshotBeforeUpdate === 'function') { if ( oldProps !== current.memoizedProps || oldState !== current.memoizedState ) { workInProgress.effectTag |= Snapshot; } } return false; } if (typeof getDerivedStateFromProps === 'function') { applyDerivedStateFromProps( workInProgress, ctor, getDerivedStateFromProps, newProps, ); newState = workInProgress.memoizedState; } const shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate( workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext, ); if (shouldUpdate) { // In order to support react-lifecycles-compat polyfilled components, // Unsafe lifecycles should not be invoked for components using the new APIs. if ( !hasNewLifecycles && (typeof instance.UNSAFE_componentWillUpdate === 'function' || typeof instance.componentWillUpdate === 'function') ) { startPhaseTimer(workInProgress, 'componentWillUpdate'); if (typeof instance.componentWillUpdate === 'function') { instance.componentWillUpdate(newProps, newState, nextContext); } if (typeof instance.UNSAFE_componentWillUpdate === 'function') { instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext); } stopPhaseTimer(); } if (typeof instance.componentDidUpdate === 'function') { workInProgress.effectTag |= Update; } if (typeof instance.getSnapshotBeforeUpdate === 'function') { workInProgress.effectTag |= Snapshot; } } else { // If an update was already in progress, we should schedule an Update // effect even though we're bailing out, so that cWU/cDU are called. if (typeof instance.componentDidUpdate === 'function') { if ( oldProps !== current.memoizedProps || oldState !== current.memoizedState ) { workInProgress.effectTag |= Update; } } if (typeof instance.getSnapshotBeforeUpdate === 'function') { if ( oldProps !== current.memoizedProps || oldState !== current.memoizedState ) { workInProgress.effectTag |= Snapshot; } } // If shouldComponentUpdate returned false, we should still update the // memoized props/state to indicate that this work can be reused. workInProgress.memoizedProps = newProps; workInProgress.memoizedState = newState; } // Update the existing instance's state, props, and context pointers even // if shouldComponentUpdate returns false. instance.props = newProps; instance.state = newState; instance.context = nextContext; return shouldUpdate; }
总结一下
- 分为两个主要阶段,挂载阶段和更新阶段,两个阶段处理不太一样
- 挂载阶段会实例化组件,挂载updater等,关联instace和fiber
- 更新阶段主要是调用更新阶段的个别生命周期,判断是否是要更新
- 两个阶段都会调用
finishClassComponent
返回nextUnitOfWork