React Fiber class 组件
一、作用
二、构建类实例
备注
readContext()由 ReactFiberNewContext#readContext 实现getUnmaskedContext()由 ReactFiberLegacyContext#getUnmaskedContext 实现getMaskedContext()由 ReactFiberLegacyContext#getMaskedContext 实现setIsStrictModeForDevtools()由 ReactFiberDevToolsHook#setIsStrictModeForDevtools 实现cacheContext()由 ReactFiberLegacyContext#cacheContext 实现
function constructClassInstance(
workInProgress: Fiber,
ctor: any,
props: any,
): any {
let isLegacyContextConsumer = false;
let unmaskedContext = emptyContextObject;
let context = emptyContextObject;
const contextType = ctor.contextType;
if (__DEV__) {
if ('contextType' in ctor) {
const isValid =
// Allow null for conditional declaration
// 允许条件声明为 null
contextType === null ||
(contextType !== undefined &&
contextType.$$typeof === REACT_CONTEXT_TYPE);
if (!isValid && !didWarnAboutInvalidateContextType.has(ctor)) {
didWarnAboutInvalidateContextType.add(ctor);
let addendum = '';
if (contextType === undefined) {
addendum =
' However, it is set to undefined. ' +
'This can be caused by a typo or by mixing up named and default imports. ' +
'This can also happen due to a circular dependency, so ' +
'try moving the createContext() call to a separate file.';
} else if (typeof contextType !== 'object') {
addendum = ' However, it is set to a ' + typeof contextType + '.';
} else if (contextType.$$typeof === REACT_CONSUMER_TYPE) {
addendum = ' Did you accidentally pass the Context.Consumer instead?';
} else {
addendum =
' However, it is set to an object with keys {' +
Object.keys(contextType).join(', ') +
'}.';
}
console.error(
'%s defines an invalid contextType. ' +
'contextType should point to the Context object returned by React.createContext().%s',
getComponentNameFromType(ctor) || 'Component',
addendum,
);
}
}
}
if (typeof contextType === 'object' && contextType !== null) {
context = readContext(contextType as any);
} else if (!disableLegacyContext) {
unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
const contextTypes = ctor.contextTypes;
isLegacyContextConsumer =
contextTypes !== null && contextTypes !== undefined;
context = isLegacyContextConsumer
? getMaskedContext(workInProgress, unmaskedContext)
: emptyContextObject;
}
let instance = new ctor(props, context);
// Instantiate twice to help detect side-effects.
// 实例化两次以帮助检测副作用。
if (__DEV__) {
if (workInProgress.mode & StrictLegacyMode) {
setIsStrictModeForDevtools(true);
try {
instance = new ctor(props, context);
} finally {
setIsStrictModeForDevtools(false);
}
}
}
const state = (workInProgress.memoizedState =
instance.state !== null && instance.state !== undefined
? instance.state
: null);
instance.updater = classComponentUpdater;
workInProgress.stateNode = instance;
// The instance needs access to the fiber so that it can schedule updates
// 这个实例需要访问 fiber,以便它可以调度更新
setInstance(instance, workInProgress);
if (__DEV__) {
instance._reactInternalInstance = fakeInternalInstance;
}
if (__DEV__) {
if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) {
const componentName = getComponentNameFromType(ctor) || 'Component';
if (!didWarnAboutUninitializedState.has(componentName)) {
didWarnAboutUninitializedState.add(componentName);
console.error(
'`%s` uses `getDerivedStateFromProps` but its initial state is ' +
'%s. This is not recommended. Instead, define the initial state by ' +
'assigning an object to `this.state` in the constructor of `%s`. ' +
'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.',
componentName,
instance.state === null ? 'null' : 'undefined',
componentName,
);
}
}
// If new component APIs are defined, "unsafe" lifecycles won't be called.
// Warn about these lifecycles if they are present.
// Don't warn about react-lifecycles-compat polyfilled methods though.
//
// 如果定义了新的组件 API,“不安全”的生命周期将不会被调用。
// 如果存在这些生命周期,则发出警告。
// 但是不要对 react-lifecycles-compat 填充的方法发出警告。
if (
typeof ctor.getDerivedStateFromProps === 'function' ||
typeof instance.getSnapshotBeforeUpdate === 'function'
) {
let foundWillMountName = null;
let foundWillReceivePropsName = null;
let foundWillUpdateName = null;
if (
typeof instance.componentWillMount === 'function' &&
instance.componentWillMount.__suppressDeprecationWarning !== true
) {
foundWillMountName = 'componentWillMount';
} else if (typeof instance.UNSAFE_componentWillMount === 'function') {
foundWillMountName = 'UNSAFE_componentWillMount';
}
if (
typeof instance.componentWillReceiveProps === 'function' &&
instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
) {
foundWillReceivePropsName = 'componentWillReceiveProps';
} else if (
typeof instance.UNSAFE_componentWillReceiveProps === 'function'
) {
foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
}
if (
typeof instance.componentWillUpdate === 'function' &&
instance.componentWillUpdate.__suppressDeprecationWarning !== true
) {
foundWillUpdateName = 'componentWillUpdate';
} else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
foundWillUpdateName = 'UNSAFE_componentWillUpdate';
}
if (
foundWillMountName !== null ||
foundWillReceivePropsName !== null ||
foundWillUpdateName !== null
) {
const componentName = getComponentNameFromType(ctor) || 'Component';
const newApiName =
typeof ctor.getDerivedStateFromProps === 'function'
? 'getDerivedStateFromProps()'
: 'getSnapshotBeforeUpdate()';
if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(componentName)) {
didWarnAboutLegacyLifecyclesAndDerivedState.add(componentName);
console.error(
'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
'%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://react.dev/link/unsafe-component-lifecycles',
componentName,
newApiName,
foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
foundWillReceivePropsName !== null
? `\n ${foundWillReceivePropsName}`
: '',
foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
);
}
}
}
}
// Cache unmasked context so we can avoid recreating masked context unless necessary.
// ReactFiberLegacyContext usually updates this cache but can't for newly-created instances.
//
// 缓存未屏蔽的上下文,这样我们可以避免在不必要的情况下重新创建屏蔽的上下文。
// ReactFiberLegacyContext 通常会更新此缓存,但不能对新创建的实例进行更新。
if (isLegacyContextConsumer) {
cacheContext(workInProgress, unmaskedContext, context);
}
return instance;
}
三、挂载实例类
备注
initializeUpdateQueue()由 ReactFiberClassUpdateQueue#initializeUpdateQueue 实现readContext()由 ReactFiberNewContext#readContext 实现getUnmaskedContext()由 ReactFiberLegacyContext#getUnmaskedContext 实现getMaskedContext()由 ReactFiberLegacyContext#getMaskedContext 实现processUpdateQueue()由 ReactFiberClassUpdateQueue#processUpdateQueue 实现suspendIfUpdateReadFromEntangledAsyncAction()由 ReactFiberClassUpdateQueue#suspendIfUpdateReadFromEntangledAsyncAction 实现
// Invokes the mount life-cycles on a previously never rendered instance.
// 调用之前从未渲染过的实例的挂载生命周期。
function mountClassInstance(
workInProgress: Fiber,
ctor: any,
newProps: any,
renderLanes: Lanes,
): void {
if (__DEV__) {
checkClassInstance(workInProgress, ctor, newProps);
}
const instance = workInProgress.stateNode;
instance.props = newProps;
instance.state = workInProgress.memoizedState;
instance.refs = {};
initializeUpdateQueue(workInProgress);
const contextType = ctor.contextType;
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);
}
if (__DEV__) {
if (instance.state === newProps) {
const componentName = getComponentNameFromType(ctor) || 'Component';
if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) {
didWarnAboutDirectlyAssigningPropsToState.add(componentName);
console.error(
'%s: It is not recommended to assign props directly to state ' +
"because updates to props won't be reflected in state. " +
'In most cases, it is better to use props directly.',
componentName,
);
}
}
if (workInProgress.mode & StrictLegacyMode) {
ReactStrictModeWarnings.recordLegacyContextWarning(
workInProgress,
instance,
);
}
ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(
workInProgress,
instance,
);
}
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.
//
// 为了支持 react-lifecycles-compat 提供的 polyfilled 组件,
// 对于使用新 API 的组件,不应调用不安全的生命周期方法。
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.
//
// 如果在这个生命周期中我们有额外的状态更新,那么现在就处理它们。
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
suspendIfUpdateReadFromEntangledAsyncAction();
instance.state = workInProgress.memoizedState;
}
if (typeof instance.componentDidMount === 'function') {
workInProgress.flags |= Update | LayoutStatic;
}
if (__DEV__ && (workInProgress.mode & StrictEffectsMode) !== NoMode) {
workInProgress.flags |= MountLayoutDev;
}
}
四、恢复挂载类实例
备注
readContext()由 ReactFiberNewContext#readContext 实现getUnmaskedContext()由 ReactFiberLegacyContext#getUnmaskedContext 实现getMaskedContext()由 ReactFiberLegacyContext#getMaskedContext 实现resetHasForceUpdateBeforeProcessing()由 [ReactFiberClassUpdateQueue#resetHasForceUpdateBeforeProcessing] 实现processUpdateQueue()由 ReactFiberClassUpdateQueue#processUpdateQueue 实现suspendIfUpdateReadFromEntangledAsyncAction()由 ReactFiberClassUpdateQueue#suspendIfUpdateReadFromEntangledAsyncAction 实现hasContextChanged()由 ReactFiberLegacyContext#hasContextChanged 实现checkHasForceUpdateAfterProcessing()由 ReactFiberClassUpdateQueue#checkHasForceUpdateAfterProcessing 实现
function resumeMountClassInstance(
workInProgress: Fiber,
ctor: any,
newProps: any,
renderLanes: Lanes,
): boolean {
const instance = workInProgress.stateNode;
const unresolvedOldProps = workInProgress.memoizedProps;
const oldProps = resolveClassComponentProps(ctor, unresolvedOldProps);
instance.props = oldProps;
const oldContext = instance.context;
const contextType = ctor.contextType;
let nextContext = emptyContextObject;
if (typeof contextType === 'object' && contextType !== null) {
nextContext = readContext(contextType);
} else if (!disableLegacyContext) {
const nextLegacyUnmaskedContext = getUnmaskedContext(
workInProgress,
ctor,
true,
);
nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext);
}
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
const hasNewLifecycles =
typeof getDerivedStateFromProps === 'function' ||
typeof instance.getSnapshotBeforeUpdate === 'function';
// When comparing whether props changed, we should compare using the
// unresolved props object that is stored on the fiber, rather than the
// one that gets assigned to the instance, because that object may have been
// cloned to resolve default props and/or remove `ref`.
//
// 在比较 props 是否变化时,我们应该使用存储在 fiber 上的未解析 props 对象进行比较,而
// 不是分配给实例的那个对象,因为该对象可能已被克隆以解析默认 props 和/或移除 `ref`。
const unresolvedNewProps = workInProgress.pendingProps;
const didReceiveNewProps = unresolvedNewProps !== unresolvedOldProps;
// 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.
//
// 注意:在这些生命周期中,instance.props/instance.state 是之前尝试渲染时的值,而不
// 是“当前”的值。然而,在 componentDidUpdate 中,我们传递的是“当前”的 props。
// In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for components using the new APIs.
//
// 为了支持 react-lifecycles-compat 提供的 polyfilled 组件,
// 对于使用新 API 的组件,不应调用不安全的生命周期方法。
if (
!hasNewLifecycles &&
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
typeof instance.componentWillReceiveProps === 'function')
) {
if (didReceiveNewProps || oldContext !== nextContext) {
callComponentWillReceiveProps(
workInProgress,
instance,
newProps,
nextContext,
);
}
}
resetHasForceUpdateBeforeProcessing();
const oldState = workInProgress.memoizedState;
let newState = (instance.state = oldState);
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
suspendIfUpdateReadFromEntangledAsyncAction();
newState = workInProgress.memoizedState;
if (
!didReceiveNewProps &&
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.
//
// 如果更新已经在进行中,即使我们要中止,也应该安排一个 Update
// 效果,这样 cWU/cDU 才会被调用。
if (typeof instance.componentDidMount === 'function') {
workInProgress.flags |= Update | LayoutStatic;
}
if (__DEV__ && (workInProgress.mode & StrictEffectsMode) !== NoMode) {
workInProgress.flags |= MountLayoutDev;
}
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.
//
// 为了支持 react-lifecycles-compat 提供的填充组件,
// 使用新 API 的组件不应调用不安全的生命周期方法。
if (
!hasNewLifecycles &&
(typeof instance.UNSAFE_componentWillMount === 'function' ||
typeof instance.componentWillMount === 'function')
) {
if (typeof instance.componentWillMount === 'function') {
instance.componentWillMount();
}
if (typeof instance.UNSAFE_componentWillMount === 'function') {
instance.UNSAFE_componentWillMount();
}
}
if (typeof instance.componentDidMount === 'function') {
workInProgress.flags |= Update | LayoutStatic;
}
if (__DEV__ && (workInProgress.mode & StrictEffectsMode) !== NoMode) {
workInProgress.flags |= MountLayoutDev;
}
} 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.
//
// 如果更新已经在进行中,即使我们要退出,也应该安排一次更新效果,以便调用 cWU/cDU。
if (typeof instance.componentDidMount === 'function') {
workInProgress.flags |= Update | LayoutStatic;
}
if (__DEV__ && (workInProgress.mode & StrictEffectsMode) !== NoMode) {
workInProgress.flags |= MountLayoutDev;
}
// If shouldComponentUpdate returned false, we should still update the
// memoized state to indicate that this work can be reused.
//
// 如果 shouldComponentUpdate 返回 false,我们仍然应该更新缓存的状态,以表明这项工作可以被重复使用。
workInProgress.memoizedProps = newProps;
workInProgress.memoizedState = newState;
}
// Update the existing instance's state, props, and context pointers even
// if shouldComponentUpdate returns false.
//
// 更新现有实例的状态、属性和上下文指针,即使 shouldComponentUpdate 返回 false。
instance.props = newProps;
instance.state = newState;
instance.context = nextContext;
return shouldUpdate;
}
五、更新类实例
备注
cloneUpdateQueue()由 [ReactFiberClassUpdateQueue#cloneUpdateQueue] 实现readContext()由 ReactFiberNewContext#readContext 实现getUnmaskedContext()由 ReactFiberLegacyContext#getUnmaskedContext 实现getMaskedContext()由 ReactFiberLegacyContext#getMaskedContext 实现resetHasForceUpdateBeforeProcessing()由 [ReactFiberClassUpdateQueue#resetHasForceUpdateBeforeProcessing] 实现processUpdateQueue()由 ReactFiberClassUpdateQueue#processUpdateQueue 实现suspendIfUpdateReadFromEntangledAsyncAction()由 ReactFiberClassUpdateQueue#suspendIfUpdateReadFromEntangledAsyncAction 实现hasContextChanged()由 ReactFiberLegacyContext#hasContextChanged 实现checkHasForceUpdateAfterProcessing()由 ReactFiberClassUpdateQueue#checkHasForceUpdateAfterProcessing 实现checkIfContextChanged()由 [ReactFiberNewContext#checkIfContextChanged] 实现
// Invokes the update life-cycles and returns false if it shouldn't rerender.
// 调用更新生命周期,如果不应该重新渲染则返回 false。
function updateClassInstance(
current: Fiber,
workInProgress: Fiber,
ctor: any,
newProps: any,
renderLanes: Lanes,
): boolean {
const instance = workInProgress.stateNode;
cloneUpdateQueue(current, workInProgress);
const unresolvedOldProps = workInProgress.memoizedProps;
const oldProps = resolveClassComponentProps(ctor, unresolvedOldProps);
instance.props = oldProps;
const unresolvedNewProps = workInProgress.pendingProps;
const oldContext = instance.context;
const contextType = ctor.contextType;
let nextContext = emptyContextObject;
if (typeof contextType === 'object' && contextType !== null) {
nextContext = readContext(contextType);
} else if (!disableLegacyContext) {
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.
//
// 注意:在这些生命周期中,instance.props/instance.state 是之前尝试渲染时的值,而
// 不是“当前”的值。然而,在 componentDidUpdate 中,我们传递的是“当前”的 props。
// In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for components using the new APIs.
//
// 为了支持 react-lifecycles-compat 提供的 polyfilled 组件,
// 对于使用新 API 的组件,不应调用不安全的生命周期方法。
if (
!hasNewLifecycles &&
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
typeof instance.componentWillReceiveProps === 'function')
) {
if (
unresolvedOldProps !== unresolvedNewProps ||
oldContext !== nextContext
) {
callComponentWillReceiveProps(
workInProgress,
instance,
newProps,
nextContext,
);
}
}
resetHasForceUpdateBeforeProcessing();
const oldState = workInProgress.memoizedState;
let newState = (instance.state = oldState);
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
suspendIfUpdateReadFromEntangledAsyncAction();
newState = workInProgress.memoizedState;
if (
unresolvedOldProps === unresolvedNewProps &&
oldState === newState &&
!hasContextChanged() &&
!checkHasForceUpdateAfterProcessing() &&
!(
current !== null &&
current.dependencies !== null &&
checkIfContextChanged(current.dependencies)
)
) {
// 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.
//
// 如果更新已经在进行中,即使我们要退出,也应该安排一次更新效果,以便调用 cWU/cDU。
if (typeof instance.componentDidUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Update;
}
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= 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,
) ||
// TODO: In some cases, we'll end up checking if context has changed twice,
// both before and after `shouldComponentUpdate` has been called. Not ideal,
// but I'm loath to refactor this function. This only happens for memoized
// components so it's not that common.
//
// TODO:在某些情况下,我们最终会检查上下文是否发生了变化两次,
// 分别在 `shouldComponentUpdate` 被调用之前和之后。这样不太理想,
// 但我不愿意重构这个函数。这只会发生在记忆化组件上,所以并不常见。
(current !== null &&
current.dependencies !== null &&
checkIfContextChanged(current.dependencies));
if (shouldUpdate) {
// In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for components using the new APIs.
//
// 为了支持 react-lifecycles-compat 提供的 polyfilled 组件,
// 对于使用新 API 的组件,不应调用不安全的生命周期方法。
if (
!hasNewLifecycles &&
(typeof instance.UNSAFE_componentWillUpdate === 'function' ||
typeof instance.componentWillUpdate === 'function')
) {
if (typeof instance.componentWillUpdate === 'function') {
instance.componentWillUpdate(newProps, newState, nextContext);
}
if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
}
}
if (typeof instance.componentDidUpdate === 'function') {
workInProgress.flags |= Update;
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
workInProgress.flags |= 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.
//
// 如果更新已经在进行中,即使我们要退出,也应该安排一次更新效果,以便调用 cWU/cDU。
if (typeof instance.componentDidUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Update;
}
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Snapshot;
}
}
// If shouldComponentUpdate returned false, we should still update the
// memoized props/state to indicate that this work can be reused.
//
// 如果 shouldComponentUpdate 返回 false,我们仍然应该更新
// memoized 的 props/state,以表明这项工作可以被重用。
workInProgress.memoizedProps = newProps;
workInProgress.memoizedState = newState;
}
// Update the existing instance's state, props, and context pointers even
// if shouldComponentUpdate returns false.
//
// 更新现有实例的状态、属性和上下文指针,即使 shouldComponentUpdate 返回 false。
instance.props = newProps;
instance.state = newState;
instance.context = nextContext;
return shouldUpdate;
}
六、解析类组件属性
export function resolveClassComponentProps(
Component: any,
baseProps: Object,
): Object {
let newProps = baseProps;
// Remove ref from the props object, if it exists.
// 如果存在,从 props 对象中移除 ref。
if ('ref' in baseProps) {
newProps = {} as any;
for (const propName in baseProps) {
if (propName !== 'ref') {
newProps[propName] = baseProps[propName];
}
}
}
// Resolve default props.
const defaultProps = Component.defaultProps;
if (defaultProps) {
// We may have already copied the props object above to remove ref. If so,
// we can modify that. Otherwise, copy the props object with Object.assign.
//
// 我们可能已经在上面复制了 props 对象以移除 ref。如果是这样,
// 我们可以修改它。否则,使用 Object.assign 复制 props 对象。
if (newProps === baseProps) {
newProps = assign({}, newProps);
}
// Taken from old JSX runtime, where this used to live.
// 取自旧的 JSX 运行时,以前它就在这里。
for (const propName in defaultProps) {
if (newProps[propName] === undefined) {
newProps[propName] = defaultProps[propName];
}
}
}
return newProps;
}
七、常量
1. 模拟内部实例
const fakeInternalInstance = {};
2. 类组件更新器
备注
createUpdate()由 ReactFiberClassUpdateQueue#createUpdate 实现enqueueUpdate()由 ReactFiberClassUpdateQueue#enqueueUpdate 实现startUpdateTimerByLane()由 ReactProfilerTimer#startUpdateTimerByLane 实现scheduleUpdateOnFiber()由 ReactFiberWorkLoop 提供entangleTransitions()由 ReactFiberClassUpdateQueue#entangleTransitions 实现markStateUpdateScheduled()由 ReactFiberDevToolsHook#markStateUpdateScheduled 实现requestUpdateLane()由 ReactFiberWorkLoop 提供
const classComponentUpdater = {
// 入队设置状态
enqueueSetState(inst: any, payload: any, callback) {
const fiber = getInstance(inst);
const lane = requestUpdateLane(fiber);
const update = createUpdate(lane);
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback);
}
update.callback = callback;
}
const root = enqueueUpdate(fiber, update, lane);
if (root !== null) {
startUpdateTimerByLane(lane, 'this.setState()', fiber);
scheduleUpdateOnFiber(root, fiber, lane);
entangleTransitions(root, fiber, lane);
}
if (enableSchedulingProfiler) {
markStateUpdateScheduled(fiber, lane);
}
},
// 入队替换状态
enqueueReplaceState(inst: any, payload: any, callback: null) {
const fiber = getInstance(inst);
const lane = requestUpdateLane(fiber);
const update = createUpdate(lane);
update.tag = ReplaceState;
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback);
}
update.callback = callback;
}
const root = enqueueUpdate(fiber, update, lane);
if (root !== null) {
startUpdateTimerByLane(lane, 'this.replaceState()', fiber);
scheduleUpdateOnFiber(root, fiber, lane);
entangleTransitions(root, fiber, lane);
}
if (enableSchedulingProfiler) {
markStateUpdateScheduled(fiber, lane);
}
},
// 强制更新入队
enqueueForceUpdate(inst: any, callback) {
const fiber = getInstance(inst);
const lane = requestUpdateLane(fiber);
const update = createUpdate(lane);
update.tag = ForceUpdate;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback);
}
update.callback = callback;
}
const root = enqueueUpdate(fiber, update, lane);
if (root !== null) {
startUpdateTimerByLane(lane, 'this.forceUpdate()', fiber);
scheduleUpdateOnFiber(root, fiber, lane);
entangleTransitions(root, fiber, lane);
}
if (enableSchedulingProfiler) {
markForceUpdateScheduled(fiber, lane);
}
},
};
八、变量
// 已警告关于组件的状态分配
let didWarnAboutStateAssignmentForComponent;
// 已警告未初始化状态
let didWarnAboutUninitializedState;
// 警告在没有 didUpdate 的情况下使用 getSnapshotBeforeUpdate
let didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
// 已警告有关旧生命周期和派生状态
let didWarnAboutLegacyLifecyclesAndDerivedState;
// 已警告关于未定义的派生状态
let didWarnAboutUndefinedDerivedState;
// 警告过关于直接将 props 分配给 state
let didWarnAboutDirectlyAssigningPropsToState;
// 已警告关于 contextType 和 contextTypes
let didWarnAboutContextTypeAndContextTypes;
// 已警告关于上下文类型
let didWarnAboutContextTypes;
// 已警告有关子上下文类型
let didWarnAboutChildContextTypes;
// 已警告有关无效上下文类型
let didWarnAboutInvalidateContextType;
// 已在无效回调上发出警告
let didWarnOnInvalidCallback;
if (__DEV__) {
didWarnAboutStateAssignmentForComponent = new Set<string>();
didWarnAboutUninitializedState = new Set<string>();
didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set<string>();
didWarnAboutLegacyLifecyclesAndDerivedState = new Set<string>();
didWarnAboutDirectlyAssigningPropsToState = new Set<string>();
didWarnAboutUndefinedDerivedState = new Set<string>();
didWarnAboutContextTypeAndContextTypes = new Set<string>();
didWarnAboutContextTypes = new Set<mixed>();
didWarnAboutChildContextTypes = new Set<mixed>();
didWarnAboutInvalidateContextType = new Set<string>();
didWarnOnInvalidCallback = new Set<string>();
Object.freeze(fakeInternalInstance);
}
九、工具
1. 警告无效回调
function warnOnInvalidCallback(callback: mixed) {
if (__DEV__) {
if (callback === null || typeof callback === 'function') {
return;
}
const key = String(callback);
if (!didWarnOnInvalidCallback.has(key)) {
didWarnOnInvalidCallback.add(key);
console.error(
'Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback,
);
}
}
}
2. 警告未定义的派生状态
function warnOnUndefinedDerivedState(type: any, partialState: any) {
if (__DEV__) {
if (partialState === undefined) {
const componentName = getComponentNameFromType(type) || 'Component';
if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
didWarnAboutUndefinedDerivedState.add(componentName);
console.error(
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
'You have returned undefined.',
componentName,
);
}
}
}
}
3. 应用派生状态自属性
备注
setIsStrictModeForDevtools()由 ReactFiberDevToolsHook#setIsStrictModeForDevtools 实现
function applyDerivedStateFromProps(
workInProgress: Fiber,
ctor: any,
getDerivedStateFromProps: (props: any, state: any) => any,
nextProps: any,
) {
const prevState = workInProgress.memoizedState;
let partialState = getDerivedStateFromProps(nextProps, prevState);
if (__DEV__) {
if (workInProgress.mode & StrictLegacyMode) {
setIsStrictModeForDevtools(true);
try {
// Invoke the function an extra time to help detect side-effects.
// 再额外调用一次函数以帮助检测副作用。
partialState = getDerivedStateFromProps(nextProps, prevState);
} finally {
setIsStrictModeForDevtools(false);
}
}
warnOnUndefinedDerivedState(ctor, partialState);
}
// Merge the partial state and the previous state.
// 合并部分状态和之前的状态。
const memoizedState =
partialState === null || partialState === undefined
? prevState
: assign({}, prevState, partialState);
workInProgress.memoizedState = memoizedState;
// Once the update queue is empty, persist the derived state onto the
// base state.
//
// 一旦更新队列为空,将派生状态持久化到基础状态上。
if (workInProgress.lanes === NoLanes) {
// Queue is always non-null for classes
// 对于类来说,队列始终不为 null
const updateQueue: UpdateQueue<any> = workInProgress.updateQueue as any;
updateQueue.baseState = memoizedState;
}
}
4. 检查应否更新组件
备注
setIsStrictModeForDevtools()由 ReactFiberDevToolsHook#setIsStrictModeForDevtools 实现
function checkShouldComponentUpdate(
workInProgress: Fiber,
ctor: any,
oldProps: any,
newProps: any,
oldState: any,
newState: any,
nextContext: any,
) {
const instance = workInProgress.stateNode;
if (typeof instance.shouldComponentUpdate === 'function') {
let shouldUpdate = instance.shouldComponentUpdate(
newProps,
newState,
nextContext,
);
if (__DEV__) {
if (workInProgress.mode & StrictLegacyMode) {
setIsStrictModeForDevtools(true);
try {
// Invoke the function an extra time to help detect side-effects.
// 再调用一次函数以帮助检测副作用。
shouldUpdate = instance.shouldComponentUpdate(
newProps,
newState,
nextContext,
);
} finally {
setIsStrictModeForDevtools(false);
}
}
if (shouldUpdate === undefined) {
console.error(
'%s.shouldComponentUpdate(): Returned undefined instead of a ' +
'boolean value. Make sure to return true or false.',
getComponentNameFromType(ctor) || 'Component',
);
}
}
return shouldUpdate;
}
if (ctor.prototype && ctor.prototype.isPureReactComponent) {
return (
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
);
}
return true;
}
5. 检查类实例
function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
const instance = workInProgress.stateNode;
if (__DEV__) {
const name = getComponentNameFromType(ctor) || 'Component';
const renderPresent = instance.render;
if (!renderPresent) {
if (ctor.prototype && typeof ctor.prototype.render === 'function') {
console.error(
'No `render` method found on the %s ' +
'instance: did you accidentally return an object from the constructor?',
name,
);
} else {
console.error(
'No `render` method found on the %s ' +
'instance: you may have forgotten to define `render`.',
name,
);
}
}
if (
instance.getInitialState &&
!instance.getInitialState.isReactClassApproved &&
!instance.state
) {
console.error(
'getInitialState was defined on %s, a plain JavaScript class. ' +
'This is only supported for classes created using React.createClass. ' +
'Did you mean to define a state property instead?',
name,
);
}
if (
instance.getDefaultProps &&
!instance.getDefaultProps.isReactClassApproved
) {
console.error(
'getDefaultProps was defined on %s, a plain JavaScript class. ' +
'This is only supported for classes created using React.createClass. ' +
'Use a static property to define defaultProps instead.',
name,
);
}
if (instance.contextType) {
console.error(
'contextType was defined as an instance property on %s. Use a static ' +
'property to define contextType instead.',
name,
);
}
if (disableLegacyContext) {
if (ctor.childContextTypes && !didWarnAboutChildContextTypes.has(ctor)) {
didWarnAboutChildContextTypes.add(ctor);
console.error(
'%s uses the legacy childContextTypes API which was removed in React 19. ' +
'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
name,
);
}
if (ctor.contextTypes && !didWarnAboutContextTypes.has(ctor)) {
didWarnAboutContextTypes.add(ctor);
console.error(
'%s uses the legacy contextTypes API which was removed in React 19. ' +
'Use React.createContext() with static contextType instead. ' +
'(https://react.dev/link/legacy-context)',
name,
);
}
} else {
if (instance.contextTypes) {
console.error(
'contextTypes was defined as an instance property on %s. Use a static ' +
'property to define contextTypes instead.',
name,
);
}
if (
ctor.contextType &&
ctor.contextTypes &&
!didWarnAboutContextTypeAndContextTypes.has(ctor)
) {
didWarnAboutContextTypeAndContextTypes.add(ctor);
console.error(
'%s declares both contextTypes and contextType static properties. ' +
'The legacy contextTypes property will be ignored.',
name,
);
}
if (ctor.childContextTypes && !didWarnAboutChildContextTypes.has(ctor)) {
didWarnAboutChildContextTypes.add(ctor);
console.error(
'%s uses the legacy childContextTypes API which will soon be removed. ' +
'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
name,
);
}
if (ctor.contextTypes && !didWarnAboutContextTypes.has(ctor)) {
didWarnAboutContextTypes.add(ctor);
console.error(
'%s uses the legacy contextTypes API which will soon be removed. ' +
'Use React.createContext() with static contextType instead. ' +
'(https://react.dev/link/legacy-context)',
name,
);
}
}
if (typeof instance.componentShouldUpdate === 'function') {
console.error(
'%s has a method called ' +
'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
'The name is phrased as a question because the function is ' +
'expected to return a value.',
name,
);
}
if (
ctor.prototype &&
ctor.prototype.isPureReactComponent &&
typeof instance.shouldComponentUpdate !== 'undefined'
) {
console.error(
'%s has a method called shouldComponentUpdate(). ' +
'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
'Please extend React.Component if shouldComponentUpdate is used.',
getComponentNameFromType(ctor) || 'A pure component',
);
}
if (typeof instance.componentDidUnmount === 'function') {
console.error(
'%s has a method called ' +
'componentDidUnmount(). But there is no such lifecycle method. ' +
'Did you mean componentWillUnmount()?',
name,
);
}
if (typeof instance.componentDidReceiveProps === 'function') {
console.error(
'%s has a method called ' +
'componentDidReceiveProps(). But there is no such lifecycle method. ' +
'If you meant to update the state in response to changing props, ' +
'use componentWillReceiveProps(). If you meant to fetch data or ' +
'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
name,
);
}
if (typeof instance.componentWillRecieveProps === 'function') {
console.error(
'%s has a method called ' +
'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?',
name,
);
}
if (typeof instance.UNSAFE_componentWillRecieveProps === 'function') {
console.error(
'%s has a method called ' +
'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?',
name,
);
}
const hasMutatedProps = instance.props !== newProps;
if (instance.props !== undefined && hasMutatedProps) {
console.error(
'When calling super() in `%s`, make sure to pass ' +
"up the same props that your component's constructor was passed.",
name,
);
}
if (instance.defaultProps) {
console.error(
'Setting defaultProps as an instance property on %s is not supported and will be ignored.' +
' Instead, define defaultProps as a static property on %s.',
name,
name,
);
}
if (
typeof instance.getSnapshotBeforeUpdate === 'function' &&
typeof instance.componentDidUpdate !== 'function' &&
!didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(ctor)
) {
didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(ctor);
console.error(
'%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' +
'This component defines getSnapshotBeforeUpdate() only.',
getComponentNameFromType(ctor),
);
}
if (typeof instance.getDerivedStateFromProps === 'function') {
console.error(
'%s: getDerivedStateFromProps() is defined as an instance method ' +
'and will be ignored. Instead, declare it as a static method.',
name,
);
}
if (typeof instance.getDerivedStateFromError === 'function') {
console.error(
'%s: getDerivedStateFromError() is defined as an instance method ' +
'and will be ignored. Instead, declare it as a static method.',
name,
);
}
if (typeof ctor.getSnapshotBeforeUpdate === 'function') {
console.error(
'%s: getSnapshotBeforeUpdate() is defined as a static method ' +
'and will be ignored. Instead, declare it as an instance method.',
name,
);
}
const state = instance.state;
if (state && (typeof state !== 'object' || isArray(state))) {
console.error('%s.state: must be set to an object or null', name);
}
if (
typeof instance.getChildContext === 'function' &&
typeof ctor.childContextTypes !== 'object'
) {
console.error(
'%s.getChildContext(): childContextTypes must be defined in order to ' +
'use getChildContext().',
name,
);
}
}
}
6. 调用组件将要挂载
备注
getComponentNameFromFiber()由 getComponentNameFromFiber 实现
function callComponentWillMount(workInProgress: Fiber, instance: any) {
const oldState = instance.state;
if (typeof instance.componentWillMount === 'function') {
instance.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);
}
}
7. 调用组件将接收属性
备注
getComponentNameFromFiber()由 getComponentNameFromFiber 实现
function callComponentWillReceiveProps(
workInProgress: Fiber,
instance: any,
newProps: any,
nextContext: any,
) {
const oldState = instance.state;
if (typeof instance.componentWillReceiveProps === 'function') {
instance.componentWillReceiveProps(newProps, nextContext);
}
if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
}
if (instance.state !== oldState) {
if (__DEV__) {
const componentName =
getComponentNameFromFiber(workInProgress) || 'Component';
if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
didWarnAboutStateAssignmentForComponent.add(componentName);
console.error(
'%s.componentWillReceiveProps(): Assigning directly to ' +
"this.state is deprecated (except inside a component's " +
'constructor). Use setState instead.',
componentName,
);
}
}
classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
}
}