React Fiber 提交副作用
专门供 ReactFiberCommitWork 使用的方法集。
一、作用
二、提交钩子布局效果
备注
startEffectTimer()由 ReactProfilerTimer#startEffectTimer 实现recordEffectDuration()由 ReactProfilerTimer#recordEffectDuration 实现
export function commitHookLayoutEffects(
finishedWork: Fiber,
hookFlags: HookFlags,
) {
// At this point layout effects have already been destroyed (during mutation phase).
// This is done to prevent sibling component effects from interfering with each other,
// e.g. a destroy function in one component should never override a ref set
// by a create function in another component during the same commit.
//
// 此时布局效果已被销毁(在变更阶段期间)。
// 这样做是为了防止兄弟组件的效果相互干扰,
// 例如,一个组件中的销毁函数绝不应覆盖另一个组件在同一次提交期间由创建函数设置的引用。
if (shouldProfile(finishedWork)) {
startEffectTimer();
commitHookEffectListMount(hookFlags, finishedWork);
recordEffectDuration(finishedWork);
} else {
commitHookEffectListMount(hookFlags, finishedWork);
}
}
三、提交钩子布局卸载效果
备注
startEffectTimer()由 ReactProfilerTimer#startEffectTimer 实现recordEffectDuration()由 ReactProfilerTimer#recordEffectDuration 实现
export function commitHookLayoutUnmountEffects(
finishedWork: Fiber,
nearestMountedAncestor: null | Fiber,
hookFlags: HookFlags,
) {
// Layout effects are destroyed during the mutation phase so that all
// destroy functions for all fibers are called before any create functions.
// This prevents sibling component effects from interfering with each other,
// e.g. a destroy function in one component should never override a ref set
// by a create function in another component during the same commit.
//
// 布局效果在变更阶段会被销毁,以确保所有 fiber 的销毁函数在任何创建函数之前被调用。
// 这可以防止兄弟组件的效果互相干扰,
// 例如,一个组件中的销毁函数不应覆盖同次提交中另一个组件创建函数设置的 ref。
if (shouldProfile(finishedWork)) {
startEffectTimer();
commitHookEffectListUnmount(
hookFlags,
finishedWork,
nearestMountedAncestor,
);
recordEffectDuration(finishedWork);
} else {
commitHookEffectListUnmount(
hookFlags,
finishedWork,
nearestMountedAncestor,
);
}
}
四、提交钩子效果列表挂载
备注
markComponentPassiveEffectMountStarted()由 ReactFiberDevToolsHook#markComponentPassiveEffectMountStarted 实现markComponentLayoutEffectMountStarted()由 ReactFiberDevToolsHook#markComponentLayoutEffectMountStarted 实现setIsRunningInsertionEffect()由 ReactFiberWorkLoop 提供runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现callCreateInDEV()由 ReactFiberCallUserSpace#callCreateInDEV 实现markComponentPassiveEffectMountStopped()由 ReactFiberDevToolsHook#markComponentPassiveEffectMountStopped 实现markComponentLayoutEffectMountStopped()由 ReactFiberDevToolsHook#markComponentLayoutEffectMountStopped 实现captureCommitPhaseError()由 ReactFiberWorkLoop 提供
export function commitHookEffectListMount(
flags: HookFlags,
finishedWork: Fiber,
) {
try {
const updateQueue: FunctionComponentUpdateQueue | null =
finishedWork.updateQueue as any;
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
if ((effect.tag & flags) === flags) {
if (enableSchedulingProfiler) {
if ((flags & HookPassive) !== NoHookEffect) {
markComponentPassiveEffectMountStarted(finishedWork);
} else if ((flags & HookLayout) !== NoHookEffect) {
markComponentLayoutEffectMountStarted(finishedWork);
}
}
// Mount
let destroy;
if (__DEV__) {
if ((flags & HookInsertion) !== NoHookEffect) {
setIsRunningInsertionEffect(true);
}
destroy = runWithFiberInDEV(finishedWork, callCreateInDEV, effect);
if ((flags & HookInsertion) !== NoHookEffect) {
setIsRunningInsertionEffect(false);
}
} else {
const create = effect.create;
const inst = effect.inst;
destroy = create();
inst.destroy = destroy;
}
if (enableSchedulingProfiler) {
if ((flags & HookPassive) !== NoHookEffect) {
markComponentPassiveEffectMountStopped();
} else if ((flags & HookLayout) !== NoHookEffect) {
markComponentLayoutEffectMountStopped();
}
}
if (__DEV__) {
if (destroy !== undefined && typeof destroy !== 'function') {
let hookName;
if ((effect.tag & HookLayout) !== NoFlags) {
hookName = 'useLayoutEffect';
} else if ((effect.tag & HookInsertion) !== NoFlags) {
hookName = 'useInsertionEffect';
} else {
hookName = 'useEffect';
}
let addendum;
if (destroy === null) {
addendum =
' You returned null. If your effect does not require clean ' +
'up, return undefined (or nothing).';
} else if (typeof destroy.then === 'function') {
addendum =
'\n\nIt looks like you wrote ' +
hookName +
'(async () => ...) or returned a Promise. ' +
'Instead, write the async function inside your effect ' +
'and call it immediately:\n\n' +
hookName +
'(() => {\n' +
' async function fetchData() {\n' +
' // You can await here\n' +
' const response = await MyAPI.getData(someId);\n' +
' // ...\n' +
' }\n' +
' fetchData();\n' +
`}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
} else {
addendum = ' You returned: ' + destroy;
}
runWithFiberInDEV(
finishedWork,
(n, a) => {
console.error(
'%s must not return anything besides a function, ' +
'which is used for clean-up.%s',
n,
a,
);
},
hookName,
addendum,
);
}
}
}
effect = effect.next;
} while (effect !== firstEffect);
}
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
五、提交钩子效果列表卸载
备注
markComponentPassiveEffectUnmountStarted()由 ReactFiberDevToolsHook#markComponentPassiveEffectUnmountStarted 实现markComponentLayoutEffectUnmountStarted()由 ReactFiberDevToolsHook#markComponentLayoutEffectUnmountStarted 实现setIsRunningInsertionEffect()由 ReactFiberWorkLoop 提供markComponentPassiveEffectUnmountStopped()由 ReactFiberDevToolsHook#markComponentPassiveEffectUnmountStopped 实现markComponentLayoutEffectUnmountStopped()由 ReactFiberDevToolsHook#markComponentLayoutEffectUnmountStopped 实现captureCommitPhaseError()由 ReactFiberWorkLoop 提供
export function commitHookEffectListUnmount(
flags: HookFlags,
finishedWork: Fiber,
nearestMountedAncestor: Fiber | null,
) {
try {
const updateQueue: FunctionComponentUpdateQueue | null =
finishedWork.updateQueue as any;
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
if ((effect.tag & flags) === flags) {
// Unmount
// 卸载
const inst = effect.inst;
const destroy = inst.destroy;
if (destroy !== undefined) {
inst.destroy = undefined;
if (enableSchedulingProfiler) {
if ((flags & HookPassive) !== NoHookEffect) {
markComponentPassiveEffectUnmountStarted(finishedWork);
} else if ((flags & HookLayout) !== NoHookEffect) {
markComponentLayoutEffectUnmountStarted(finishedWork);
}
}
if (__DEV__) {
if ((flags & HookInsertion) !== NoHookEffect) {
setIsRunningInsertionEffect(true);
}
}
safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
if (__DEV__) {
if ((flags & HookInsertion) !== NoHookEffect) {
setIsRunningInsertionEffect(false);
}
}
if (enableSchedulingProfiler) {
if ((flags & HookPassive) !== NoHookEffect) {
markComponentPassiveEffectUnmountStopped();
} else if ((flags & HookLayout) !== NoHookEffect) {
markComponentLayoutEffectUnmountStopped();
}
}
}
}
effect = effect.next;
} while (effect !== firstEffect);
}
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
六、提交钩子被动挂载效果
备注
startEffectTimer()由 ReactProfilerTimer#startEffectTimer 实现recordEffectDuration()由 ReactProfilerTimer#recordEffectDuration 实现
export function commitHookPassiveMountEffects(
finishedWork: Fiber,
hookFlags: HookFlags,
) {
if (shouldProfile(finishedWork)) {
startEffectTimer();
commitHookEffectListMount(hookFlags, finishedWork);
recordEffectDuration(finishedWork);
} else {
commitHookEffectListMount(hookFlags, finishedWork);
}
}
七、提交钩子被动卸载效果
备注
startEffectTimer()由 ReactProfilerTimer#startEffectTimer 实现recordEffectDuration()由 ReactProfilerTimer#recordEffectDuration 实现
export function commitHookPassiveUnmountEffects(
finishedWork: Fiber,
nearestMountedAncestor: null | Fiber,
hookFlags: HookFlags,
) {
if (shouldProfile(finishedWork)) {
startEffectTimer();
commitHookEffectListUnmount(
hookFlags,
finishedWork,
nearestMountedAncestor,
);
recordEffectDuration(finishedWork);
} else {
commitHookEffectListUnmount(
hookFlags,
finishedWork,
nearestMountedAncestor,
);
}
}
八、提交类布局生命周期
备注
getComponentNameFromFiber()由 getComponentNameFromFiber 实现startEffectTimer()由 ReactProfilerTimer#startEffectTimer 实现runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现callComponentDidMountInDEV()由 ReactFiberCallUserSpace#callComponentDidMountInDEV 实现captureCommitPhaseError()由 ReactFiberWorkLoop 提供recordEffectDuration()由 ReactProfilerTimer#recordEffectDuration 实现resolveClassComponentProps()由 ReactFiberClassComponent#resolveClassComponentProps 实现
export function commitClassLayoutLifecycles(
finishedWork: Fiber,
current: Fiber | null,
) {
const instance = finishedWork.stateNode;
if (current === null) {
// We could update instance props and state here,
// but instead we rely on them being set during last render.
// TODO: revisit this when we implement resuming.
//
// 我们可以在这里更新实例的属性和状态,但我们依赖于它们在上一次渲染时已被设置。
// 待办事项:在我们实现恢复功能时重新审视这一点。
if (__DEV__) {
if (
!finishedWork.type.defaultProps &&
!('ref' in finishedWork.memoizedProps) &&
!didWarnAboutReassigningProps
) {
if (instance.props !== finishedWork.memoizedProps) {
console.error(
'Expected %s props to match memoized props before ' +
'componentDidMount. ' +
'This might either be because of a bug in React, or because ' +
'a component reassigns its own `this.props`. ' +
'Please file an issue.',
getComponentNameFromFiber(finishedWork) || 'instance',
);
}
if (instance.state !== finishedWork.memoizedState) {
console.error(
'Expected %s state to match memoized state before ' +
'componentDidMount. ' +
'This might either be because of a bug in React, or because ' +
'a component reassigns its own `this.state`. ' +
'Please file an issue.',
getComponentNameFromFiber(finishedWork) || 'instance',
);
}
}
}
if (shouldProfile(finishedWork)) {
startEffectTimer();
if (__DEV__) {
runWithFiberInDEV(
finishedWork,
callComponentDidMountInDEV,
finishedWork,
instance,
);
} else {
try {
instance.componentDidMount();
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
recordEffectDuration(finishedWork);
} else {
if (__DEV__) {
runWithFiberInDEV(
finishedWork,
callComponentDidMountInDEV,
finishedWork,
instance,
);
} else {
try {
instance.componentDidMount();
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
} else {
const prevProps = resolveClassComponentProps(
finishedWork.type,
current.memoizedProps,
);
const prevState = current.memoizedState;
// We could update instance props and state here,
// but instead we rely on them being set during last render.
// TODO: revisit this when we implement resuming.
//
// 我们可以在这里更新实例的属性和状态,但我们依赖于它们在上一次渲染时已被设置。
// 待办事项:在我们实现恢复功能时重新审视这一点。
if (__DEV__) {
if (
!finishedWork.type.defaultProps &&
!('ref' in finishedWork.memoizedProps) &&
!didWarnAboutReassigningProps
) {
if (instance.props !== finishedWork.memoizedProps) {
console.error(
'Expected %s props to match memoized props before ' +
'componentDidUpdate. ' +
'This might either be because of a bug in React, or because ' +
'a component reassigns its own `this.props`. ' +
'Please file an issue.',
getComponentNameFromFiber(finishedWork) || 'instance',
);
}
if (instance.state !== finishedWork.memoizedState) {
console.error(
'Expected %s state to match memoized state before ' +
'componentDidUpdate. ' +
'This might either be because of a bug in React, or because ' +
'a component reassigns its own `this.state`. ' +
'Please file an issue.',
getComponentNameFromFiber(finishedWork) || 'instance',
);
}
}
}
if (shouldProfile(finishedWork)) {
startEffectTimer();
if (__DEV__) {
runWithFiberInDEV(
finishedWork,
callComponentDidUpdateInDEV,
finishedWork,
instance,
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate,
);
} else {
try {
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate,
);
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
recordEffectDuration(finishedWork);
} else {
if (__DEV__) {
runWithFiberInDEV(
finishedWork,
callComponentDidUpdateInDEV,
finishedWork,
instance,
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate,
);
} else {
try {
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate,
);
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
}
}
九、提交 Class DidMount
备注
runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现callComponentDidMountInDEV()由 ReactFiberCallUserSpace#callComponentDidMountInDEV 实现captureCommitPhaseError()由 ReactFiberWorkLoop 提供
export function commitClassDidMount(finishedWork: Fiber) {
// TODO: Check for LayoutStatic flag
// 待办:检查 LayoutStatic 标志
const instance = finishedWork.stateNode;
if (typeof instance.componentDidMount === 'function') {
if (__DEV__) {
runWithFiberInDEV(
finishedWork,
callComponentDidMountInDEV,
finishedWork,
instance,
);
} else {
try {
instance.componentDidMount();
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
}
十、提交类回调
备注
getComponentNameFromFiber()由 getComponentNameFromFiber 实现runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现commitCallbacks()由 ReactFiberClassUpdateQueue#commitCallbackscaptureCommitPhaseError()由 ReactFiberWorkLoop 提供
export function commitClassCallbacks(finishedWork: Fiber) {
// TODO: I think this is now always non-null by the time it reaches the
// commit phase. Consider removing the type check.
//
// 待办:我认为到达提交阶段时这个值现在总是非空的。考虑移除类型检查。
const updateQueue: UpdateQueue<mixed> | null =
finishedWork.updateQueue as any;
if (updateQueue !== null) {
const instance = finishedWork.stateNode;
if (__DEV__) {
if (
!finishedWork.type.defaultProps &&
!('ref' in finishedWork.memoizedProps) &&
!didWarnAboutReassigningProps
) {
if (instance.props !== finishedWork.memoizedProps) {
console.error(
'Expected %s props to match memoized props before ' +
'processing the update queue. ' +
'This might either be because of a bug in React, or because ' +
'a component reassigns its own `this.props`. ' +
'Please file an issue.',
getComponentNameFromFiber(finishedWork) || 'instance',
);
}
if (instance.state !== finishedWork.memoizedState) {
console.error(
'Expected %s state to match memoized state before ' +
'processing the update queue. ' +
'This might either be because of a bug in React, or because ' +
'a component reassigns its own `this.state`. ' +
'Please file an issue.',
getComponentNameFromFiber(finishedWork) || 'instance',
);
}
}
}
// We could update instance props and state here,
// but instead we rely on them being set during last render.
// TODO: revisit this when we implement resuming.
//
// 我们可以在这里更新实例的属性和状态,
// 但我们依赖于它们在上一次渲染期间已被设置。
// 待办事项:在我们实现恢复功能时重新审视这一点。
try {
if (__DEV__) {
runWithFiberInDEV(finishedWork, commitCallbacks, updateQueue, instance);
} else {
commitCallbacks(updateQueue, instance);
}
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
十一、提交隐藏类回调
备注
runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现commitHiddenCallbacks()由 ReactFiberClassUpdateQueue#commitHiddenCallbacks 实现captureCommitPhaseError()由 ReactFiberWorkLoop 提供
export function commitClassHiddenCallbacks(finishedWork: Fiber) {
// Commit any callbacks that would have fired while the component
// was hidden.
//
// 提交在组件隐藏时可能触发的任何回调。
const updateQueue: UpdateQueue<mixed> | null =
finishedWork.updateQueue as any;
if (updateQueue !== null) {
const instance = finishedWork.stateNode;
try {
if (__DEV__) {
runWithFiberInDEV(
finishedWork,
commitHiddenCallbacks,
updateQueue,
instance,
);
} else {
commitHiddenCallbacks(updateQueue, instance);
}
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
十二、提交根回调
备注
getPublicInstance()由宿主环境提供runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现commitCallbacks()由 ReactFiberClassUpdateQueue#commitCallbacks 实现captureCommitPhaseError()由 ReactFiberWorkLoop 提供
export function commitRootCallbacks(finishedWork: Fiber) {
// TODO: I think this is now always non-null by the time it reaches the
// commit phase. Consider removing the type check.
//
// 待办事项:我认为在到达提交阶段时,这个值现在总是非空的。考虑删除类型检查。
const updateQueue: UpdateQueue<mixed> | null =
finishedWork.updateQueue as any;
if (updateQueue !== null) {
let instance = null;
if (finishedWork.child !== null) {
switch (finishedWork.child.tag) {
case HostSingleton:
case HostComponent:
instance = getPublicInstance(finishedWork.child.stateNode);
break;
case ClassComponent:
instance = finishedWork.child.stateNode;
break;
}
}
try {
if (__DEV__) {
runWithFiberInDEV(finishedWork, commitCallbacks, updateQueue, instance);
} else {
commitCallbacks(updateQueue, instance);
}
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
十三、提交类快照
备注
getComponentNameFromFiber()由 getComponentNameFromFiber 提供resolveClassComponentProps()由 ReactFiberClassComponent#resolveClassComponentProps 实现runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现captureCommitPhaseError()由 ReactFiberWorkLoop 实现
export function commitClassSnapshot(finishedWork: Fiber, current: Fiber) {
const prevProps = current.memoizedProps;
const prevState = current.memoizedState;
const instance = finishedWork.stateNode;
// We could update instance props and state here,
// but instead we rely on them being set during last render.
// TODO: revisit this when we implement resuming.
//
// 我们本可以在此更新实例的属性和状态,
// 但我们选择依赖它们在上一次渲染时已被设置。
// TODO: 在实现恢复功能时再重新考虑这一点。
if (__DEV__) {
if (
!finishedWork.type.defaultProps &&
!('ref' in finishedWork.memoizedProps) &&
!didWarnAboutReassigningProps
) {
if (instance.props !== finishedWork.memoizedProps) {
console.error(
'Expected %s props to match memoized props before ' +
'getSnapshotBeforeUpdate. ' +
'This might either be because of a bug in React, or because ' +
'a component reassigns its own `this.props`. ' +
'Please file an issue.',
getComponentNameFromFiber(finishedWork) || 'instance',
);
}
if (instance.state !== finishedWork.memoizedState) {
console.error(
'Expected %s state to match memoized state before ' +
'getSnapshotBeforeUpdate. ' +
'This might either be because of a bug in React, or because ' +
'a component reassigns its own `this.state`. ' +
'Please file an issue.',
getComponentNameFromFiber(finishedWork) || 'instance',
);
}
}
}
try {
const resolvedPrevProps = resolveClassComponentProps(
finishedWork.type,
prevProps,
);
let snapshot;
if (__DEV__) {
snapshot = runWithFiberInDEV(
finishedWork,
callGetSnapshotBeforeUpdates,
instance,
resolvedPrevProps,
prevState,
);
const didWarnSet =
didWarnAboutUndefinedSnapshotBeforeUpdate as any as Set<mixed>;
if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
didWarnSet.add(finishedWork.type);
runWithFiberInDEV(finishedWork, () => {
console.error(
'%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
'must be returned. You have returned undefined.',
getComponentNameFromFiber(finishedWork),
);
});
}
} else {
snapshot = callGetSnapshotBeforeUpdates(
instance,
resolvedPrevProps,
prevState,
);
}
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
十四、安全调用组件卸载
备注
resolveClassComponentProps()由 ReactFiberClassComponent#resolveClassComponentProps 实现startEffectTimer()由 ReactProfilerTimer#startEffectTimer 实现runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现callComponentWillUnmountInDEV()由 ReactFiberCallUserSpace#callComponentWillUnmountInDEV 实现captureCommitPhaseError()由 ReactFiberWorkLoop 实现
// 捕获错误,以免在卸载时中断。
// Capture errors so they don't interrupt unmounting.
export function safelyCallComponentWillUnmount(
current: Fiber,
nearestMountedAncestor: Fiber | null,
instance: any,
) {
instance.props = resolveClassComponentProps(
current.type,
current.memoizedProps,
);
instance.state = current.memoizedState;
if (shouldProfile(current)) {
startEffectTimer();
if (__DEV__) {
runWithFiberInDEV(
current,
callComponentWillUnmountInDEV,
current,
nearestMountedAncestor,
instance,
);
} else {
try {
instance.componentWillUnmount();
} catch (error) {
captureCommitPhaseError(current, nearestMountedAncestor, error);
}
}
recordEffectDuration(current);
} else {
if (__DEV__) {
runWithFiberInDEV(
current,
callComponentWillUnmountInDEV,
current,
nearestMountedAncestor,
instance,
);
} else {
try {
instance.componentWillUnmount();
} catch (error) {
captureCommitPhaseError(current, nearestMountedAncestor, error);
}
}
}
}
十五、安全附加引用
备注
runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现captureCommitPhaseError()由 ReactFiberWorkLoop 提供
// Capture errors so they don't interrupt mounting.
// 捕获错误,以免中断挂载。
export function safelyAttachRef(
current: Fiber,
nearestMountedAncestor: Fiber | null,
) {
try {
if (__DEV__) {
runWithFiberInDEV(current, commitAttachRef, current);
} else {
commitAttachRef(current);
}
} catch (error) {
captureCommitPhaseError(current, nearestMountedAncestor, error);
}
}
十六、安全地分离引用
备注
startEffectTimer()由 ReactProfilerTimer#startEffectTimer 实现recordEffectDuration()由 ReactProfilerTimer#recordEffectDuration 实现runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现captureCommitPhaseError()由 ReactFiberWorkLoop 提供
export function safelyDetachRef(
current: Fiber,
nearestMountedAncestor: Fiber | null,
) {
const ref = current.ref;
const refCleanup = current.refCleanup;
if (ref !== null) {
if (typeof refCleanup === 'function') {
try {
if (shouldProfile(current)) {
try {
startEffectTimer();
if (__DEV__) {
runWithFiberInDEV(current, refCleanup);
} else {
refCleanup();
}
} finally {
recordEffectDuration(current);
}
} else {
if (__DEV__) {
runWithFiberInDEV(current, refCleanup);
} else {
refCleanup();
}
}
} catch (error) {
captureCommitPhaseError(current, nearestMountedAncestor, error);
} finally {
// `refCleanup` has been called. Nullify all references to it to prevent double invocation.
// `refCleanup` 已被调用。将所有对它的引用置空以防止重复调用。
current.refCleanup = null;
const finishedWork = current.alternate;
if (finishedWork != null) {
finishedWork.refCleanup = null;
}
}
} else if (typeof ref === 'function') {
try {
if (shouldProfile(current)) {
try {
startEffectTimer();
if (__DEV__) {
runWithFiberInDEV(current, ref, null) as void;
} else {
ref(null);
}
} finally {
recordEffectDuration(current);
}
} else {
if (__DEV__) {
runWithFiberInDEV(current, ref, null) as void;
} else {
ref(null);
}
}
} catch (error) {
captureCommitPhaseError(current, nearestMountedAncestor, error);
}
} else {
ref.current = null;
}
}
}
十七、提交性能分析器更新
备注
runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现captureCommitPhaseError()由 ReactFiberWorkLoop 实现
export function commitProfilerUpdate(
finishedWork: Fiber,
current: Fiber | null,
commitStartTime: number,
effectDuration: number,
) {
if (enableProfilerTimer) {
try {
if (__DEV__) {
runWithFiberInDEV(
finishedWork,
commitProfiler,
finishedWork,
current,
commitStartTime,
effectDuration,
);
} else {
commitProfiler(finishedWork, current, commitStartTime, effectDuration);
}
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
十八、提交分析器后提交
备注
runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现captureCommitPhaseError()由 ReactFiberWorkLoop 提供
export function commitProfilerPostCommit(
finishedWork: Fiber,
current: Fiber | null,
commitStartTime: number,
passiveEffectDuration: number,
) {
try {
if (__DEV__) {
runWithFiberInDEV(
finishedWork,
commitProfilerPostCommitImpl,
finishedWork,
current,
commitStartTime,
passiveEffectDuration,
);
} else {
commitProfilerPostCommitImpl(
finishedWork,
current,
commitStartTime,
passiveEffectDuration,
);
}
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
十九、变量
// 已警告关于未定义的快照更新前
let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
if (__DEV__) {
didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
}
二十、工具
1. 是否应配置档案
function shouldProfile(current: Fiber): boolean {
return (
enableProfilerTimer &&
enableProfilerCommitHooks &&
(current.mode & ProfileMode) !== NoMode
);
}
2. 在更新前调用获取快照
function callGetSnapshotBeforeUpdates(
instance: any,
prevProps: any,
prevState: any,
) {
return instance.getSnapshotBeforeUpdate(prevProps, prevState);
}
3. 提交附加引用
备注
getPublicInstance()由宿主环境提供getViewTransitionName()由 ReactFiberViewTransitionComponent#getViewTransitionName 实现createFragmentInstance()由宿主环境提供startEffectTimer()由 ReactProfilerTimer#startEffectTimer 实现recordEffectDuration()由 ReactProfilerTimer#recordEffectDuration 实现getComponentNameFromFiber()由 getComponentNameFromFiber 实现
function commitAttachRef(finishedWork: Fiber) {
const ref = finishedWork.ref;
if (ref !== null) {
let instanceToUse;
switch (finishedWork.tag) {
case HostHoistable:
case HostSingleton:
case HostComponent:
instanceToUse = getPublicInstance(finishedWork.stateNode);
break;
case ViewTransitionComponent: {
if (enableViewTransition) {
const instance: ViewTransitionState = finishedWork.stateNode;
const props: ViewTransitionProps = finishedWork.memoizedProps;
const name = getViewTransitionName(props, instance);
if (instance.ref === null || instance.ref.name !== name) {
instance.ref = createViewTransitionInstance(name);
}
instanceToUse = instance.ref;
break;
}
instanceToUse = finishedWork.stateNode;
break;
}
case Fragment:
if (enableFragmentRefs) {
const instance: null | FragmentInstanceType = finishedWork.stateNode;
if (instance === null) {
finishedWork.stateNode = createFragmentInstance(finishedWork);
}
instanceToUse = finishedWork.stateNode;
break;
}
// Fall through
// 贯穿
default:
instanceToUse = finishedWork.stateNode;
}
if (typeof ref === 'function') {
if (shouldProfile(finishedWork)) {
try {
startEffectTimer();
finishedWork.refCleanup = ref(instanceToUse);
} finally {
recordEffectDuration(finishedWork);
}
} else {
finishedWork.refCleanup = ref(instanceToUse);
}
} else {
if (__DEV__) {
// TODO: We should move these warnings to happen during the render
// phase (markRef).
// 待办:我们应该将这些警告移到渲染阶段(markRef)时触发
if (typeof ref === 'string') {
console.error('String refs are no longer supported.');
} else if (!ref.hasOwnProperty('current')) {
console.error(
'Unexpected ref object provided for %s. ' +
'Use either a ref-setter function or React.createRef().',
getComponentNameFromFiber(finishedWork),
);
}
}
ref.current = instanceToUse;
}
}
}
4. 安全调用销毁
备注
runWithFiberInDEV()由 ReactCurrentFiber#runWithFiberInDEV 实现callDestroyInDEV()由 ReactFiberCallUserSpace#callDestroyInDEV 实现captureCommitPhaseError()由 ReactFiberWorkLoop 提供
function safelyCallDestroy(
current: Fiber,
nearestMountedAncestor: Fiber | null,
// destroy: (() => void) | (({...}) => void),
destroy: (() => void) | (({}) => void),
// resource?: {...} | void | null,
resource?: object | void | null,
) {
const destroy_ = resource == null ? destroy : destroy.bind(null, resource);
if (__DEV__) {
runWithFiberInDEV(
current,
callDestroyInDEV,
current,
nearestMountedAncestor,
destroy_,
);
} else {
try {
destroy_();
} catch (error) {
captureCommitPhaseError(current, nearestMountedAncestor, error);
}
}
}
5. 提交分析器
备注
isCurrentUpdateNested()由 ReactProfilerTimer#isCurrentUpdateNested 实现
function commitProfiler(
finishedWork: Fiber,
current: Fiber | null,
commitStartTime: number,
effectDuration: number,
) {
const { id, onCommit, onRender } =
finishedWork.memoizedProps as ProfilerProps;
let phase: ProfilerPhase = current === null ? 'mount' : 'update';
if (enableProfilerNestedUpdatePhase) {
if (isCurrentUpdateNested()) {
phase = 'nested-update';
}
}
if (typeof onRender === 'function') {
onRender(
id,
phase,
finishedWork.actualDuration,
finishedWork.treeBaseDuration,
finishedWork.actualStartTime,
commitStartTime,
);
}
if (enableProfilerCommitHooks) {
if (typeof onCommit === 'function') {
onCommit(id, phase, effectDuration, commitStartTime);
}
}
}
6. 提交分析器提交后实现
备注
isCurrentUpdateNested()由 ReactProfilerTimer#isCurrentUpdateNested 实现
function commitProfilerPostCommitImpl(
finishedWork: Fiber,
current: Fiber | null,
commitStartTime: number,
passiveEffectDuration: number,
): void {
const { id, onPostCommit } = finishedWork.memoizedProps;
let phase = current === null ? 'mount' : 'update';
if (enableProfilerNestedUpdatePhase) {
if (isCurrentUpdateNested()) {
phase = 'nested-update';
}
}
if (typeof onPostCommit === 'function') {
onPostCommit(id, phase, passiveEffectDuration, commitStartTime);
}
}