Skip to content

Commit 87c023b

Browse files
author
Brian Vaughn
authored
Profiler onRender only called when we do work (#19885)
If we didn't perform any work in the subtree, skip calling onRender.
1 parent 92c7e49 commit 87c023b

File tree

4 files changed

+54
-28
lines changed

4 files changed

+54
-28
lines changed

packages/react-reconciler/src/ReactFiberBeginWork.new.js

+3
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,9 @@ function updateHostComponent(
11481148
workInProgress.flags|=ContentReset;
11491149
}
11501150

1151+
// React DevTools reads this flag.
1152+
workInProgress.flags|=PerformedWork;
1153+
11511154
markRef(current,workInProgress);
11521155
reconcileChildren(current,workInProgress,nextChildren,renderLanes);
11531156
returnworkInProgress.child;

packages/react-reconciler/src/ReactFiberCompleteWork.new.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ import {
7777
LayoutMask,
7878
PassiveMask,
7979
StaticMask,
80+
PerformedWork,
8081
}from'./ReactFiberFlags';
8182
importinvariantfrom'shared/invariant';
8283

@@ -987,9 +988,13 @@ function completeWork(
987988
constflags=workInProgress.flags;
988989
letnewFlags=flags;
989990

990-
// Call onRender any time this fiber or its subtree are worked on, even
991-
// if there are no effects
992-
newFlags|=OnRenderFlag;
991+
// Call onRender any time this fiber or its subtree are worked on.
992+
if(
993+
(flags&PerformedWork)!==NoFlags||
994+
(subtreeFlags&PerformedWork)!==NoFlags
995+
){
996+
newFlags|=OnRenderFlag;
997+
}
993998

994999
// Call onCommit only if the subtree contains layout work, or if it
9951000
// contains deletions, since those might result in unmount work, which

packages/react/src/__tests__/ReactDOMTracing-test.internal.js

+21-12
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,17 @@ describe('ReactDOMTracing', () => {
151151
expect(
152152
onInteractionScheduledWorkCompleted,
153153
).toHaveBeenLastNotifiedOfInteraction(interaction);
154-
// TODO: This is 4 instead of 3 because this update was scheduled at
155-
// idle priority, and idle updates are slightly higher priority than
156-
// offscreen work. So it takes two render passes to finish it. Profiler
157-
// calls `onRender` for the first render even though everything
158-
// bails out.
159-
expect(onRender).toHaveBeenCalledTimes(4);
154+
155+
if(gate(flags=>flags.new)){
156+
expect(onRender).toHaveBeenCalledTimes(3);
157+
}else{
158+
// TODO: This is 4 instead of 3 because this update was scheduled at
159+
// idle priority, and idle updates are slightly higher priority than
160+
// offscreen work. So it takes two render passes to finish it. Profiler
161+
// calls `onRender` for the first render even though everything
162+
// bails out.
163+
expect(onRender).toHaveBeenCalledTimes(4);
164+
}
160165
expect(onRender).toHaveLastRenderedWithInteractions(
161166
newSet([interaction]),
162167
);
@@ -305,12 +310,16 @@ describe('ReactDOMTracing', () => {
305310
expect(
306311
onInteractionScheduledWorkCompleted,
307312
).toHaveBeenLastNotifiedOfInteraction(interaction);
308-
// TODO: This is 4 instead of 3 because this update was scheduled at
309-
// idle priority, and idle updates are slightly higher priority than
310-
// offscreen work. So it takes two render passes to finish it. Profiler
311-
// calls `onRender` for the first render even though everything
312-
// bails out.
313-
expect(onRender).toHaveBeenCalledTimes(4);
313+
if(gate(flags=>flags.new)){
314+
expect(onRender).toHaveBeenCalledTimes(3);
315+
}else{
316+
// TODO: This is 4 instead of 3 because this update was scheduled at
317+
// idle priority, and idle updates are slightly higher priority than
318+
// offscreen work. So it takes two render passes to finish it. Profiler
319+
// calls `onRender` for the first render even though everything
320+
// bails out.
321+
expect(onRender).toHaveBeenCalledTimes(4);
322+
}
314323
expect(onRender).toHaveLastRenderedWithInteractions(
315324
newSet([interaction]),
316325
);

packages/react/src/__tests__/ReactProfiler-test.internal.js

+22-13
Original file line numberDiff line numberDiff line change
@@ -362,24 +362,33 @@ describe('Profiler', () => {
362362

363363
Scheduler.unstable_advanceTime(20);// 10 -> 30
364364

365-
// Updating a parent should report a re-render,
366-
// since React technically did a little bit of work between the Profiler and the bailed out subtree.
367365
renderer.update(<App/>);
368366

369-
expect(callback).toHaveBeenCalledTimes(1);
367+
if(gate(flags=>flags.new)){
368+
// None of the Profiler's subtree was rendered because App bailed out before the Profiler.
369+
// So we expect onRender not to be called.
370+
expect(callback).not.toHaveBeenCalled();
371+
}else{
372+
// Updating a parent reports a re-render,
373+
// since React technically did a little bit of work between the Profiler and the bailed out subtree.
374+
// This is not optimal but it's how the old reconciler fork works.
375+
expect(callback).toHaveBeenCalledTimes(1);
370376

371-
call=callback.mock.calls[0];
377+
call=callback.mock.calls[0];
372378

373-
expect(call).toHaveLength(enableSchedulerTracing ? 7 : 6);
374-
expect(call[0]).toBe('test');
375-
expect(call[1]).toBe('update');
376-
expect(call[2]).toBe(0);// actual time
377-
expect(call[3]).toBe(10);// base time
378-
expect(call[4]).toBe(30);// start time
379-
expect(call[5]).toBe(30);// commit time
380-
expect(call[6]).toEqual(enableSchedulerTracing ? newSet() : undefined);// interaction events
379+
expect(call).toHaveLength(enableSchedulerTracing ? 7 : 6);
380+
expect(call[0]).toBe('test');
381+
expect(call[1]).toBe('update');
382+
expect(call[2]).toBe(0);// actual time
383+
expect(call[3]).toBe(10);// base time
384+
expect(call[4]).toBe(30);// start time
385+
expect(call[5]).toBe(30);// commit time
386+
expect(call[6]).toEqual(
387+
enableSchedulerTracing ? newSet() : undefined,
388+
);// interaction events
381389

382-
callback.mockReset();
390+
callback.mockReset();
391+
}
383392

384393
Scheduler.unstable_advanceTime(20);// 30 -> 50
385394

0 commit comments

Comments
 (0)
close