Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions packages/react-reconciler/src/ReactDebugFiberPerf.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,9 @@ export function stopPhaseTimer(): void {
}
}

export function startWorkLoopTimer(): void {
export function startWorkLoopTimer(nextUnitOfWork: Fiber | null): void {
if (enableUserTimingAPI) {
currentFiber = nextUnitOfWork;
if (!supportsUserTiming) {
return;
}
Expand All @@ -332,14 +333,22 @@ export function startWorkLoopTimer(): void {
}
}

export function stopWorkLoopTimer(): void {
export function stopWorkLoopTimer(interruptedBy: Fiber | null): void {
if (enableUserTimingAPI) {
if (!supportsUserTiming) {
return;
}
const warning = commitCountInCurrentWorkLoop > 1
? 'There were cascading updates'
: null;
let warning = null;
if (interruptedBy !== null) {
if (interruptedBy.tag === HostRoot) {
warning = 'A top-level update interrupted the previous render';
} else {
const componentName = getComponentName(interruptedBy) || 'Unknown';
warning = `An update to ${componentName} interrupted the previous render`;
}
} else if (commitCountInCurrentWorkLoop > 1) {
warning = 'There were cascading updates';
}
commitCountInCurrentWorkLoop = 0;
// Pause any measurements until the next loop.
pauseTimers();
Expand Down
19 changes: 13 additions & 6 deletions packages/react-reconciler/src/ReactFiberScheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
let isCommitting: boolean = false;
let isUnmounting: boolean = false;

// Used for performance tracking.
let interruptedBy: Fiber | null = null;

function resetContextStack() {
// Reset the stack
reset();
Expand Down Expand Up @@ -752,8 +755,6 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
root: FiberRoot,
expirationTime: ExpirationTime,
): Fiber | null {
startWorkLoopTimer();

invariant(
!isWorking,
'renderRoot was called recursively. This error is likely caused ' +
Expand All @@ -772,7 +773,7 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
expirationTime !== nextRenderExpirationTime ||
nextUnitOfWork === null
) {
// This is a restart. Reset the stack.
// Reset the stack and start working from the root.
resetContextStack();
nextRoot = root;
nextRenderExpirationTime = expirationTime;
Expand All @@ -783,6 +784,8 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
);
}

startWorkLoopTimer(nextUnitOfWork);

let didError = false;
let error = null;
if (__DEV__) {
Expand Down Expand Up @@ -865,12 +868,12 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
const uncaughtError = firstUncaughtError;

// We're done performing work. Time to clean up.
stopWorkLoopTimer(interruptedBy);
interruptedBy = null;
isWorking = false;
didFatal = false;
firstUncaughtError = null;

stopWorkLoopTimer();

if (uncaughtError !== null) {
onUncaughtError(uncaughtError);
}
Expand Down Expand Up @@ -1198,7 +1201,11 @@ export default function<T, P, I, TI, PI, C, CC, CX, PL>(
root === nextRoot &&
expirationTime <= nextRenderExpirationTime
) {
// This is an interruption. Restart the root from the top.
// Restart the root from the top.
if (nextUnitOfWork !== null) {
// This is an interruption. (Used for performance tracking.)
interruptedBy = fiber;
}
nextRoot = null;
nextUnitOfWork = null;
nextRenderExpirationTime = NoWork;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,4 +498,18 @@ describe('ReactDebugFiberPerf', () => {
});
expect(getFlameChart()).toMatchSnapshot();
});

it('warns if an in-progress update is interrupted', () => {
function Foo() {
return <span />;
}

ReactNoop.render(<Foo />);
ReactNoop.flushUnitsOfWork(2);
ReactNoop.flushSync(() => {
ReactNoop.render(<Foo />);
});
ReactNoop.flush();
expect(getFlameChart()).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,17 @@ exports[`ReactDebugFiberPerf skips parents during setState 1`] = `
"
`;

exports[`ReactDebugFiberPerf supports portals 1`] = `
"⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ Child [mount]

⚛ (Committing Changes)
⚛ (Committing Host Effects: 2 Total)
⚛ (Calling Lifecycle Methods: 0 Total)
"
`;

exports[`ReactDebugFiberPerf supports returns 1`] = `
"⚛ (React Tree Reconciliation)
⚛ App [mount]
Expand All @@ -260,14 +271,22 @@ exports[`ReactDebugFiberPerf supports returns 1`] = `
"
`;

exports[`ReactDebugFiberPerf supports portals 1`] = `
exports[`ReactDebugFiberPerf warns if an in-progress update is interrupted 1`] = `
"⚛ (React Tree Reconciliation)
⚛ Parent [mount]
⚛ Child [mount]
⚛ Foo [mount]

⛔ (React Tree Reconciliation) Warning: A top-level update interrupted the previous render
⚛ Foo [mount]

⚛ (Committing Changes)
⚛ (Committing Host Effects: 2 Total)
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 0 Total)

⚛ (React Tree Reconciliation)

⚛ (Committing Changes)
⚛ (Committing Host Effects: 1 Total)
⚛ (Calling Lifecycle Methods: 1 Total)
"
`;

Expand Down