mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Implemented Profiler onCommit() and onPostCommit() hooks (#17910)
* Implemented Profiler onCommit() and onPostCommit() hooks * Added enableProfilerCommitHooks feature flag for commit hooks * Moved onCommit and onPassiveCommit behind separate feature flag
This commit is contained in:
+9
-7
@@ -826,13 +826,8 @@ function createFiberFromProfiler(
|
||||
key: null | string,
|
||||
): Fiber {
|
||||
if (__DEV__) {
|
||||
if (
|
||||
typeof pendingProps.id !== 'string' ||
|
||||
typeof pendingProps.onRender !== 'function'
|
||||
) {
|
||||
console.error(
|
||||
'Profiler must specify an "id" string and "onRender" function as props',
|
||||
);
|
||||
if (typeof pendingProps.id !== 'string') {
|
||||
console.error('Profiler must specify an "id" as a prop');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,6 +837,13 @@ function createFiberFromProfiler(
|
||||
fiber.type = REACT_PROFILER_TYPE;
|
||||
fiber.expirationTime = expirationTime;
|
||||
|
||||
if (enableProfilerTimer) {
|
||||
fiber.stateNode = {
|
||||
effectDuration: 0,
|
||||
passiveEffectDuration: 0,
|
||||
};
|
||||
}
|
||||
|
||||
return fiber;
|
||||
}
|
||||
|
||||
|
||||
@@ -580,6 +580,12 @@ function updateProfiler(
|
||||
) {
|
||||
if (enableProfilerTimer) {
|
||||
workInProgress.effectTag |= Update;
|
||||
|
||||
// Reset effect durations for the next eventual effect phase.
|
||||
// These are reset during render to allow the DevTools commit hook a chance to read them,
|
||||
const stateNode = workInProgress.stateNode;
|
||||
stateNode.effectDuration = 0;
|
||||
stateNode.passiveEffectDuration = 0;
|
||||
}
|
||||
const nextProps = workInProgress.pendingProps;
|
||||
const nextChildren = nextProps.children;
|
||||
@@ -2944,6 +2950,12 @@ function beginWork(
|
||||
if (hasChildWork) {
|
||||
workInProgress.effectTag |= Update;
|
||||
}
|
||||
|
||||
// Reset effect durations for the next eventual effect phase.
|
||||
// These are reset during render to allow the DevTools commit hook a chance to read them,
|
||||
const stateNode = workInProgress.stateNode;
|
||||
stateNode.effectDuration = 0;
|
||||
stateNode.passiveEffectDuration = 0;
|
||||
}
|
||||
break;
|
||||
case SuspenseComponent: {
|
||||
|
||||
+256
-19
@@ -29,6 +29,7 @@ import {
|
||||
deferPassiveEffectCleanupDuringUnmount,
|
||||
enableSchedulerTracing,
|
||||
enableProfilerTimer,
|
||||
enableProfilerCommitHooks,
|
||||
enableSuspenseServerRenderer,
|
||||
enableDeprecatedFlareAPI,
|
||||
enableFundamentalAPI,
|
||||
@@ -76,7 +77,14 @@ import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
|
||||
import {getStackByFiberInDevAndProd} from './ReactCurrentFiber';
|
||||
import {logCapturedError} from './ReactFiberErrorLogger';
|
||||
import {resolveDefaultProps} from './ReactFiberLazyComponent';
|
||||
import {getCommitTime} from './ReactProfilerTimer';
|
||||
import {
|
||||
getCommitTime,
|
||||
recordLayoutEffectDuration,
|
||||
recordPassiveEffectDuration,
|
||||
startLayoutEffectTimer,
|
||||
startPassiveEffectTimer,
|
||||
} from './ReactProfilerTimer';
|
||||
import {ProfileMode} from './ReactTypeOfMode';
|
||||
import {commitUpdateQueue} from './ReactUpdateQueue';
|
||||
import {
|
||||
getPublicInstance,
|
||||
@@ -113,6 +121,7 @@ import {
|
||||
markCommitTimeOfFallback,
|
||||
enqueuePendingPassiveHookEffectMount,
|
||||
enqueuePendingPassiveHookEffectUnmount,
|
||||
enqueuePendingPassiveProfilerEffect,
|
||||
} from './ReactFiberWorkLoop';
|
||||
import {
|
||||
NoEffect as NoHookEffect,
|
||||
@@ -175,7 +184,20 @@ const callComponentWillUnmountWithTimer = function(current, instance) {
|
||||
startPhaseTimer(current, 'componentWillUnmount');
|
||||
instance.props = current.memoizedProps;
|
||||
instance.state = current.memoizedState;
|
||||
instance.componentWillUnmount();
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
current.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
instance.componentWillUnmount();
|
||||
} finally {
|
||||
recordLayoutEffectDuration(current);
|
||||
}
|
||||
} else {
|
||||
instance.componentWillUnmount();
|
||||
}
|
||||
stopPhaseTimer();
|
||||
};
|
||||
|
||||
@@ -430,8 +452,31 @@ export function commitPassiveHookEffects(finishedWork: Fiber): void {
|
||||
// TODO (#17945) We should call all passive destroy functions (for all fibers)
|
||||
// before calling any create functions. The current approach only serializes
|
||||
// these for a single fiber.
|
||||
commitHookEffectListUnmount(HookPassive | HookHasEffect, finishedWork);
|
||||
commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
finishedWork.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
commitHookEffectListUnmount(
|
||||
HookPassive | HookHasEffect,
|
||||
finishedWork,
|
||||
);
|
||||
commitHookEffectListMount(
|
||||
HookPassive | HookHasEffect,
|
||||
finishedWork,
|
||||
);
|
||||
} finally {
|
||||
recordPassiveEffectDuration(finishedWork);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(
|
||||
HookPassive | HookHasEffect,
|
||||
finishedWork,
|
||||
);
|
||||
commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -440,6 +485,61 @@ export function commitPassiveHookEffects(finishedWork: Fiber): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function commitPassiveEffectDurations(
|
||||
finishedRoot: FiberRoot,
|
||||
finishedWork: Fiber,
|
||||
): void {
|
||||
if (enableProfilerTimer && enableProfilerCommitHooks) {
|
||||
// Only Profilers with work in their subtree will have an Update effect scheduled.
|
||||
if ((finishedWork.effectTag & Update) !== NoEffect) {
|
||||
switch (finishedWork.tag) {
|
||||
case Profiler: {
|
||||
const {passiveEffectDuration} = finishedWork.stateNode;
|
||||
const {id, onPostCommit} = finishedWork.memoizedProps;
|
||||
|
||||
// This value will still reflect the previous commit phase.
|
||||
// It does not get reset until the start of the next commit phase.
|
||||
const commitTime = getCommitTime();
|
||||
|
||||
if (typeof onPostCommit === 'function') {
|
||||
if (enableSchedulerTracing) {
|
||||
onPostCommit(
|
||||
id,
|
||||
finishedWork.alternate === null ? 'mount' : 'update',
|
||||
passiveEffectDuration,
|
||||
commitTime,
|
||||
finishedRoot.memoizedInteractions,
|
||||
);
|
||||
} else {
|
||||
onPostCommit(
|
||||
id,
|
||||
finishedWork.alternate === null ? 'mount' : 'update',
|
||||
passiveEffectDuration,
|
||||
commitTime,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Bubble times to the next nearest ancestor Profiler.
|
||||
// After we process that Profiler, we'll bubble further up.
|
||||
let parentFiber = finishedWork.return;
|
||||
while (parentFiber !== null) {
|
||||
if (parentFiber.tag === Profiler) {
|
||||
const parentStateNode = parentFiber.stateNode;
|
||||
parentStateNode.passiveEffectDuration += passiveEffectDuration;
|
||||
break;
|
||||
}
|
||||
parentFiber = parentFiber.return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function commitLifeCycles(
|
||||
finishedRoot: FiberRoot,
|
||||
current: Fiber | null,
|
||||
@@ -455,7 +555,20 @@ function commitLifeCycles(
|
||||
// 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.
|
||||
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
finishedWork.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
|
||||
} finally {
|
||||
recordLayoutEffectDuration(finishedWork);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
|
||||
}
|
||||
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
schedulePassiveEffects(finishedWork);
|
||||
@@ -497,7 +610,20 @@ function commitLifeCycles(
|
||||
}
|
||||
}
|
||||
}
|
||||
instance.componentDidMount();
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
finishedWork.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
instance.componentDidMount();
|
||||
} finally {
|
||||
recordLayoutEffectDuration(finishedWork);
|
||||
}
|
||||
} else {
|
||||
instance.componentDidMount();
|
||||
}
|
||||
stopPhaseTimer();
|
||||
} else {
|
||||
const prevProps =
|
||||
@@ -536,11 +662,28 @@ function commitLifeCycles(
|
||||
}
|
||||
}
|
||||
}
|
||||
instance.componentDidUpdate(
|
||||
prevProps,
|
||||
prevState,
|
||||
instance.__reactInternalSnapshotBeforeUpdate,
|
||||
);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
finishedWork.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
instance.componentDidUpdate(
|
||||
prevProps,
|
||||
prevState,
|
||||
instance.__reactInternalSnapshotBeforeUpdate,
|
||||
);
|
||||
} finally {
|
||||
recordLayoutEffectDuration(finishedWork);
|
||||
}
|
||||
} else {
|
||||
instance.componentDidUpdate(
|
||||
prevProps,
|
||||
prevState,
|
||||
instance.__reactInternalSnapshotBeforeUpdate,
|
||||
);
|
||||
}
|
||||
stopPhaseTimer();
|
||||
}
|
||||
}
|
||||
@@ -623,7 +766,10 @@ function commitLifeCycles(
|
||||
}
|
||||
case Profiler: {
|
||||
if (enableProfilerTimer) {
|
||||
const onRender = finishedWork.memoizedProps.onRender;
|
||||
const {onCommit, onRender} = finishedWork.memoizedProps;
|
||||
const {effectDuration} = finishedWork.stateNode;
|
||||
|
||||
const commitTime = getCommitTime();
|
||||
|
||||
if (typeof onRender === 'function') {
|
||||
if (enableSchedulerTracing) {
|
||||
@@ -633,7 +779,7 @@ function commitLifeCycles(
|
||||
finishedWork.actualDuration,
|
||||
finishedWork.treeBaseDuration,
|
||||
finishedWork.actualStartTime,
|
||||
getCommitTime(),
|
||||
commitTime,
|
||||
finishedRoot.memoizedInteractions,
|
||||
);
|
||||
} else {
|
||||
@@ -643,10 +789,48 @@ function commitLifeCycles(
|
||||
finishedWork.actualDuration,
|
||||
finishedWork.treeBaseDuration,
|
||||
finishedWork.actualStartTime,
|
||||
getCommitTime(),
|
||||
commitTime,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (enableProfilerCommitHooks) {
|
||||
if (typeof onCommit === 'function') {
|
||||
if (enableSchedulerTracing) {
|
||||
onCommit(
|
||||
finishedWork.memoizedProps.id,
|
||||
current === null ? 'mount' : 'update',
|
||||
effectDuration,
|
||||
commitTime,
|
||||
finishedRoot.memoizedInteractions,
|
||||
);
|
||||
} else {
|
||||
onCommit(
|
||||
finishedWork.memoizedProps.id,
|
||||
current === null ? 'mount' : 'update',
|
||||
effectDuration,
|
||||
commitTime,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule a passive effect for this Profiler to call onPostCommit hooks.
|
||||
// This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
|
||||
// because the effect is also where times bubble to parent Profilers.
|
||||
enqueuePendingPassiveProfilerEffect(finishedWork);
|
||||
|
||||
// Propagate layout effect durations to the next nearest Profiler ancestor.
|
||||
// Do not reset these values until the next render so DevTools has a chance to read them first.
|
||||
let parentFiber = finishedWork.return;
|
||||
while (parentFiber !== null) {
|
||||
if (parentFiber.tag === Profiler) {
|
||||
const parentStateNode = parentFiber.stateNode;
|
||||
parentStateNode.effectDuration += effectDuration;
|
||||
break;
|
||||
}
|
||||
parentFiber = parentFiber.return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -797,7 +981,17 @@ function commitUnmount(
|
||||
if ((tag & HookPassive) !== NoHookEffect) {
|
||||
enqueuePendingPassiveHookEffectUnmount(current, effect);
|
||||
} else {
|
||||
safelyCallDestroy(current, destroy);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
current.mode & ProfileMode
|
||||
) {
|
||||
startLayoutEffectTimer();
|
||||
safelyCallDestroy(current, destroy);
|
||||
recordLayoutEffectDuration(current);
|
||||
} else {
|
||||
safelyCallDestroy(current, destroy);
|
||||
}
|
||||
}
|
||||
}
|
||||
effect = effect.next;
|
||||
@@ -822,9 +1016,23 @@ function commitUnmount(
|
||||
runWithPriority(priorityLevel, () => {
|
||||
let effect = firstEffect;
|
||||
do {
|
||||
const destroy = effect.destroy;
|
||||
const {destroy, tag} = effect;
|
||||
if (destroy !== undefined) {
|
||||
safelyCallDestroy(current, destroy);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
current.mode & ProfileMode
|
||||
) {
|
||||
if ((tag & HookPassive) !== NoHookEffect) {
|
||||
safelyCallDestroy(current, destroy);
|
||||
} else {
|
||||
startLayoutEffectTimer();
|
||||
safelyCallDestroy(current, destroy);
|
||||
recordLayoutEffectDuration(current);
|
||||
}
|
||||
} else {
|
||||
safelyCallDestroy(current, destroy);
|
||||
}
|
||||
}
|
||||
effect = effect.next;
|
||||
} while (effect !== firstEffect);
|
||||
@@ -1366,7 +1574,23 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
|
||||
// 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.
|
||||
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
finishedWork.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
commitHookEffectListUnmount(
|
||||
HookLayout | HookHasEffect,
|
||||
finishedWork,
|
||||
);
|
||||
} finally {
|
||||
recordLayoutEffectDuration(finishedWork);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case Profiler: {
|
||||
@@ -1409,7 +1633,20 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
|
||||
// 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.
|
||||
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
finishedWork.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
|
||||
} finally {
|
||||
recordLayoutEffectDuration(finishedWork);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ClassComponent: {
|
||||
|
||||
+79
-5
@@ -24,6 +24,7 @@ import {
|
||||
enableSuspenseServerRenderer,
|
||||
replayFailedUnitOfWorkWithInvokeGuardedCallback,
|
||||
enableProfilerTimer,
|
||||
enableProfilerCommitHooks,
|
||||
enableSchedulerTracing,
|
||||
warnAboutUnmockedScheduler,
|
||||
flushSuspenseFallbacksInTests,
|
||||
@@ -139,6 +140,7 @@ import {
|
||||
commitDeletion,
|
||||
commitDetachRef,
|
||||
commitAttachRef,
|
||||
commitPassiveEffectDurations,
|
||||
commitResetTextContent,
|
||||
} from './ReactFiberCommitWork';
|
||||
import {enqueueUpdate} from './ReactUpdateQueue';
|
||||
@@ -148,6 +150,8 @@ import {createCapturedValue} from './ReactCapturedValue';
|
||||
|
||||
import {
|
||||
recordCommitTime,
|
||||
recordPassiveEffectDuration,
|
||||
startPassiveEffectTimer,
|
||||
startProfilerTimer,
|
||||
stopProfilerTimerIfRunningAndRecordDelta,
|
||||
} from './ReactProfilerTimer';
|
||||
@@ -261,6 +265,7 @@ let pendingPassiveEffectsRenderPriority: ReactPriorityLevel = NoPriority;
|
||||
let pendingPassiveEffectsExpirationTime: ExpirationTime = NoWork;
|
||||
let pendingPassiveHookEffectsMount: Array<HookEffect | Fiber> = [];
|
||||
let pendingPassiveHookEffectsUnmount: Array<HookEffect | Fiber> = [];
|
||||
let pendingPassiveProfilerEffects: Array<Fiber> = [];
|
||||
|
||||
let rootsWithPendingDiscreteUpdates: Map<
|
||||
FiberRoot,
|
||||
@@ -2209,6 +2214,19 @@ export function flushPassiveEffects() {
|
||||
}
|
||||
}
|
||||
|
||||
export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {
|
||||
if (enableProfilerTimer && enableProfilerCommitHooks) {
|
||||
pendingPassiveProfilerEffects.push(fiber);
|
||||
if (!rootDoesHavePassiveEffects) {
|
||||
rootDoesHavePassiveEffects = true;
|
||||
scheduleCallback(NormalPriority, () => {
|
||||
flushPassiveEffects();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function enqueuePendingPassiveHookEffectMount(
|
||||
fiber: Fiber,
|
||||
effect: HookEffect,
|
||||
@@ -2250,6 +2268,7 @@ function flushPassiveEffectsImpl() {
|
||||
if (rootWithPendingPassiveEffects === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const root = rootWithPendingPassiveEffects;
|
||||
const expirationTime = pendingPassiveEffectsExpirationTime;
|
||||
rootWithPendingPassiveEffects = null;
|
||||
@@ -2282,7 +2301,17 @@ function flushPassiveEffectsImpl() {
|
||||
if (typeof destroy === 'function') {
|
||||
if (__DEV__) {
|
||||
setCurrentDebugFiberInDEV(fiber);
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
startPassiveEffectTimer();
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
recordPassiveEffectDuration(fiber);
|
||||
} else {
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
}
|
||||
if (hasCaughtError()) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
const error = clearCaughtError();
|
||||
@@ -2291,7 +2320,20 @@ function flushPassiveEffectsImpl() {
|
||||
resetCurrentDebugFiberInDEV();
|
||||
} else {
|
||||
try {
|
||||
destroy();
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
destroy();
|
||||
} finally {
|
||||
recordPassiveEffectDuration(fiber);
|
||||
}
|
||||
} else {
|
||||
destroy();
|
||||
}
|
||||
} catch (error) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
captureCommitPhaseError(fiber, error);
|
||||
@@ -2299,7 +2341,6 @@ function flushPassiveEffectsImpl() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: Create new passive effects.
|
||||
let mountEffects = pendingPassiveHookEffectsMount;
|
||||
pendingPassiveHookEffectsMount = [];
|
||||
@@ -2308,7 +2349,17 @@ function flushPassiveEffectsImpl() {
|
||||
const fiber = ((mountEffects[i + 1]: any): Fiber);
|
||||
if (__DEV__) {
|
||||
setCurrentDebugFiberInDEV(fiber);
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
startPassiveEffectTimer();
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
recordPassiveEffectDuration(fiber);
|
||||
} else {
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
}
|
||||
if (hasCaughtError()) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
const error = clearCaughtError();
|
||||
@@ -2318,7 +2369,20 @@ function flushPassiveEffectsImpl() {
|
||||
} else {
|
||||
try {
|
||||
const create = effect.create;
|
||||
effect.destroy = create();
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
effect.destroy = create();
|
||||
} finally {
|
||||
recordPassiveEffectDuration(fiber);
|
||||
}
|
||||
} else {
|
||||
effect.destroy = create();
|
||||
}
|
||||
} catch (error) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
captureCommitPhaseError(fiber, error);
|
||||
@@ -2348,6 +2412,7 @@ function flushPassiveEffectsImpl() {
|
||||
captureCommitPhaseError(effect, error);
|
||||
}
|
||||
}
|
||||
|
||||
const nextNextEffect = effect.nextEffect;
|
||||
// Remove nextEffect pointer to assist GC
|
||||
effect.nextEffect = null;
|
||||
@@ -2355,6 +2420,15 @@ function flushPassiveEffectsImpl() {
|
||||
}
|
||||
}
|
||||
|
||||
if (enableProfilerTimer && enableProfilerCommitHooks) {
|
||||
let profilerEffects = pendingPassiveProfilerEffects;
|
||||
pendingPassiveProfilerEffects = [];
|
||||
for (let i = 0; i < profilerEffects.length; i++) {
|
||||
const fiber = ((profilerEffects[i]: any): Fiber);
|
||||
commitPassiveEffectDurations(root, fiber);
|
||||
}
|
||||
}
|
||||
|
||||
if (enableSchedulerTracing) {
|
||||
popInteractions(((prevInteractions: any): Set<Interaction>));
|
||||
finishPendingInteractions(root, expirationTime);
|
||||
|
||||
+76
-1
@@ -9,7 +9,11 @@
|
||||
|
||||
import type {Fiber} from './ReactFiber';
|
||||
|
||||
import {enableProfilerTimer} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
enableProfilerTimer,
|
||||
enableProfilerCommitHooks,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {Profiler} from 'shared/ReactWorkTags';
|
||||
|
||||
// Intentionally not named imports because Rollup would use dynamic dispatch for
|
||||
// CommonJS interop named imports.
|
||||
@@ -27,7 +31,9 @@ export type ProfilerTimer = {
|
||||
};
|
||||
|
||||
let commitTime: number = 0;
|
||||
let layoutEffectStartTime: number = -1;
|
||||
let profilerStartTime: number = -1;
|
||||
let passiveEffectStartTime: number = -1;
|
||||
|
||||
function getCommitTime(): number {
|
||||
return commitTime;
|
||||
@@ -77,9 +83,78 @@ function stopProfilerTimerIfRunningAndRecordDelta(
|
||||
}
|
||||
}
|
||||
|
||||
function recordLayoutEffectDuration(fiber: Fiber): void {
|
||||
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (layoutEffectStartTime >= 0) {
|
||||
const elapsedTime = now() - layoutEffectStartTime;
|
||||
|
||||
layoutEffectStartTime = -1;
|
||||
|
||||
// Store duration on the next nearest Profiler ancestor.
|
||||
let parentFiber = fiber.return;
|
||||
while (parentFiber !== null) {
|
||||
if (parentFiber.tag === Profiler) {
|
||||
const parentStateNode = parentFiber.stateNode;
|
||||
parentStateNode.effectDuration += elapsedTime;
|
||||
break;
|
||||
}
|
||||
parentFiber = parentFiber.return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function recordPassiveEffectDuration(fiber: Fiber): void {
|
||||
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (passiveEffectStartTime >= 0) {
|
||||
const elapsedTime = now() - passiveEffectStartTime;
|
||||
|
||||
passiveEffectStartTime = -1;
|
||||
|
||||
// Store duration on the next nearest Profiler ancestor.
|
||||
let parentFiber = fiber.return;
|
||||
while (parentFiber !== null) {
|
||||
if (parentFiber.tag === Profiler) {
|
||||
const parentStateNode = parentFiber.stateNode;
|
||||
if (parentStateNode !== null) {
|
||||
// Detached fibers have their state node cleared out.
|
||||
// In this case, the return pointer is also cleared out,
|
||||
// so we won't be able to report the time spent in this Profiler's subtree.
|
||||
parentStateNode.passiveEffectDuration += elapsedTime;
|
||||
}
|
||||
break;
|
||||
}
|
||||
parentFiber = parentFiber.return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startLayoutEffectTimer(): void {
|
||||
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
|
||||
return;
|
||||
}
|
||||
layoutEffectStartTime = now();
|
||||
}
|
||||
|
||||
function startPassiveEffectTimer(): void {
|
||||
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
|
||||
return;
|
||||
}
|
||||
passiveEffectStartTime = now();
|
||||
}
|
||||
|
||||
export {
|
||||
getCommitTime,
|
||||
recordCommitTime,
|
||||
recordLayoutEffectDuration,
|
||||
recordPassiveEffectDuration,
|
||||
startLayoutEffectTimer,
|
||||
startPassiveEffectTimer,
|
||||
startProfilerTimer,
|
||||
stopProfilerTimerIfRunning,
|
||||
stopProfilerTimerIfRunningAndRecordDelta,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,9 @@ export const warnAboutDeprecatedLifecycles = true;
|
||||
// Gather advanced timing metrics for Profiler subtrees.
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
|
||||
// Record durations for commit and passive effects phases.
|
||||
export const enableProfilerCommitHooks = false;
|
||||
|
||||
// Trace which interactions trigger each commit.
|
||||
export const enableSchedulerTracing = __PROFILE__;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.native-fb';
|
||||
// The rest of the flags are static for better dead code elimination.
|
||||
export const enableUserTimingAPI = __DEV__;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
export const enableProfilerCommitHooks = false;
|
||||
export const enableSchedulerTracing = __PROFILE__;
|
||||
export const enableSuspenseServerRenderer = false;
|
||||
export const enableSelectiveHydration = false;
|
||||
|
||||
@@ -17,6 +17,7 @@ export const enableUserTimingAPI = __DEV__;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
export const enableProfilerCommitHooks = false;
|
||||
export const enableSchedulerTracing = __PROFILE__;
|
||||
export const enableSuspenseServerRenderer = false;
|
||||
export const enableSelectiveHydration = false;
|
||||
|
||||
@@ -17,6 +17,7 @@ export const enableUserTimingAPI = __DEV__;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
export const enableProfilerCommitHooks = false;
|
||||
export const enableSchedulerTracing = __PROFILE__;
|
||||
export const enableSuspenseServerRenderer = false;
|
||||
export const enableSelectiveHydration = false;
|
||||
|
||||
@@ -17,6 +17,7 @@ export const enableUserTimingAPI = __DEV__;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
export const enableProfilerCommitHooks = false;
|
||||
export const enableSchedulerTracing = __PROFILE__;
|
||||
export const enableSuspenseServerRenderer = false;
|
||||
export const enableSelectiveHydration = false;
|
||||
|
||||
@@ -17,6 +17,7 @@ export const enableUserTimingAPI = __DEV__;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
export const enableProfilerCommitHooks = false;
|
||||
export const enableSchedulerTracing = __PROFILE__;
|
||||
export const enableSuspenseServerRenderer = false;
|
||||
export const enableSelectiveHydration = false;
|
||||
|
||||
@@ -17,6 +17,7 @@ export const enableUserTimingAPI = __DEV__;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
export const enableProfilerCommitHooks = false;
|
||||
export const enableSchedulerTracing = __PROFILE__;
|
||||
export const enableSuspenseServerRenderer = false;
|
||||
export const enableSelectiveHydration = false;
|
||||
|
||||
@@ -17,6 +17,7 @@ export const enableUserTimingAPI = false;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
export const enableProfilerTimer = false;
|
||||
export const enableProfilerCommitHooks = false;
|
||||
export const enableSchedulerTracing = false;
|
||||
export const enableSuspenseServerRenderer = true;
|
||||
export const enableSelectiveHydration = true;
|
||||
|
||||
@@ -35,6 +35,7 @@ export const {
|
||||
export let enableUserTimingAPI = __DEV__ && !__EXPERIMENTAL__;
|
||||
|
||||
export const enableProfilerTimer = __PROFILE__;
|
||||
export const enableProfilerCommitHooks = false;
|
||||
export const enableSchedulerTracing = __PROFILE__;
|
||||
export const enableSchedulerDebugging = true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user