Skip to content

Commit 7b971c0

Browse files
authored
Current behavior for excluding Component render with unchanged props from Components track (facebook#34822)
If we rerender with the same props, the render time will not be accounted for in the Components track. The attached test reproduces the behavior observed in https://codesandbox.io/p/sandbox/patient-fast-j94f2g: <img width="1118" height="354" alt="CleanShot 2025-10-13 at 00 13 41@2x" src="https://github.com/user-attachments/assets/4be10ee9-d529-4d98-9035-4f26f9587f52" />
1 parent 83ea655 commit 7b971c0

1 file changed

Lines changed: 125 additions & 10 deletions

File tree

packages/react-reconciler/src/__tests__/ReactPerformanceTrack-test.js

Lines changed: 125 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,20 @@ let act;
1515
let useEffect;
1616

1717
describe('ReactPerformanceTracks', () => {
18+
const performanceMeasureCalls = [];
19+
1820
beforeEach(() => {
21+
performanceMeasureCalls.length = 0;
1922
Object.defineProperty(performance, 'measure', {
20-
value: jest.fn(),
23+
value: jest.fn((measureName, reusableOptions) => {
24+
performanceMeasureCalls.push([
25+
measureName,
26+
{
27+
// React will mutate the options it passes to performance.measure.
28+
...reusableOptions,
29+
},
30+
]);
31+
}),
2132
configurable: true,
2233
});
2334
console.timeStamp = () => {};
@@ -32,6 +43,19 @@ describe('ReactPerformanceTracks', () => {
3243
useEffect = React.useEffect;
3344
});
3445

46+
function getConsoleTimestampEntries() {
47+
try {
48+
return console.timeStamp.mock.calls.filter(call => {
49+
const [, startTime, endTime] = call;
50+
51+
const isRegisterTrackCall = startTime !== 0.003 && endTime !== 0.003;
52+
return isRegisterTrackCall;
53+
});
54+
} finally {
55+
console.timeStamp.mockClear();
56+
}
57+
}
58+
3559
// @gate __DEV__ && enableComponentPerformanceTrack
3660
it('shows a hint if an update is triggered by a deeply equal object', async () => {
3761
const App = function App({items}) {
@@ -45,7 +69,7 @@ describe('ReactPerformanceTracks', () => {
4569
ReactNoop.render(<App items={items} />);
4670
});
4771

48-
expect(performance.measure.mock.calls).toEqual([
72+
expect(performanceMeasureCalls).toEqual([
4973
[
5074
'Mount',
5175
{
@@ -62,14 +86,14 @@ describe('ReactPerformanceTracks', () => {
6286
},
6387
],
6488
]);
65-
performance.measure.mockClear();
89+
performanceMeasureCalls.length = 0;
6690

6791
Scheduler.unstable_advanceTime(10);
6892
await act(() => {
6993
ReactNoop.render(<App items={items.concat('4')} />);
7094
});
7195

72-
expect(performance.measure.mock.calls).toEqual([
96+
expect(performanceMeasureCalls).toEqual([
7397
[
7498
'​App',
7599
{
@@ -105,7 +129,7 @@ describe('ReactPerformanceTracks', () => {
105129
ReactNoop.render(<App items={items} />);
106130
});
107131

108-
expect(performance.measure.mock.calls).toEqual([
132+
expect(performanceMeasureCalls).toEqual([
109133
[
110134
'Mount',
111135
{
@@ -122,14 +146,14 @@ describe('ReactPerformanceTracks', () => {
122146
},
123147
],
124148
]);
125-
performance.measure.mockClear();
149+
performanceMeasureCalls.length = 0;
126150

127151
Scheduler.unstable_advanceTime(10);
128152
await act(() => {
129153
ReactNoop.render(<App items={items.concat('-1')} />);
130154
});
131155

132-
expect(performance.measure.mock.calls).toEqual([
156+
expect(performanceMeasureCalls).toEqual([
133157
[
134158
'​App',
135159
{
@@ -171,7 +195,7 @@ describe('ReactPerformanceTracks', () => {
171195
ReactNoop.render(<App data={{buffer: null}} />);
172196
});
173197

174-
expect(performance.measure.mock.calls).toEqual([
198+
expect(performanceMeasureCalls).toEqual([
175199
[
176200
'Mount',
177201
{
@@ -188,7 +212,7 @@ describe('ReactPerformanceTracks', () => {
188212
},
189213
],
190214
]);
191-
performance.measure.mockClear();
215+
performanceMeasureCalls.length = 0;
192216

193217
Scheduler.unstable_advanceTime(10);
194218

@@ -197,7 +221,7 @@ describe('ReactPerformanceTracks', () => {
197221
ReactNoop.render(<App data={{buffer: bigData}} />);
198222
});
199223

200-
expect(performance.measure.mock.calls).toEqual([
224+
expect(performanceMeasureCalls).toEqual([
201225
[
202226
'​App',
203227
{
@@ -324,4 +348,95 @@ describe('ReactPerformanceTracks', () => {
324348
],
325349
]);
326350
});
351+
352+
// @gate __DEV__ && enableComponentPerformanceTrack
353+
it('includes spans for Components with no prop changes', async () => {
354+
function Left({value}) {
355+
Scheduler.unstable_advanceTime(5000);
356+
}
357+
function Right() {
358+
Scheduler.unstable_advanceTime(10000);
359+
}
360+
361+
await act(() => {
362+
ReactNoop.render(
363+
<>
364+
<Left value={1} />
365+
<Right />
366+
</>,
367+
);
368+
});
369+
370+
expect(performanceMeasureCalls).toEqual([
371+
[
372+
'Mount',
373+
{
374+
detail: {
375+
devtools: {
376+
color: 'warning',
377+
properties: null,
378+
tooltipText: 'Mount',
379+
track: 'Components ⚛',
380+
},
381+
},
382+
end: 5000,
383+
start: 0,
384+
},
385+
],
386+
[
387+
'Mount',
388+
{
389+
detail: {
390+
devtools: {
391+
color: 'warning',
392+
properties: null,
393+
tooltipText: 'Mount',
394+
track: 'Components ⚛',
395+
},
396+
},
397+
end: 15000,
398+
start: 5000,
399+
},
400+
],
401+
]);
402+
performanceMeasureCalls.length = 0;
403+
getConsoleTimestampEntries();
404+
405+
Scheduler.unstable_advanceTime(1000);
406+
407+
await act(() => {
408+
ReactNoop.render(
409+
<>
410+
<Left value={2} />
411+
<Right />
412+
</>,
413+
);
414+
});
415+
416+
expect(performanceMeasureCalls).toEqual([
417+
[
418+
'​Left',
419+
{
420+
detail: {
421+
devtools: {
422+
color: 'error',
423+
properties: [
424+
['Changed Props', ''],
425+
['– value', '1'],
426+
['+ value', '2'],
427+
],
428+
tooltipText: 'Left',
429+
track: 'Components ⚛',
430+
},
431+
},
432+
end: 21000,
433+
start: 16000,
434+
},
435+
],
436+
]);
437+
expect(getConsoleTimestampEntries()).toEqual([
438+
['Render', 16000, 31000, 'Blocking', 'Scheduler ⚛', 'primary-dark'],
439+
]);
440+
performanceMeasureCalls.length = 0;
441+
});
327442
});

0 commit comments

Comments
 (0)