Skip to content

Commit bdf1871

Browse files
authored
[Flight] Emit Deduped Server Components Marker (#31737)
Stacked on #31736. <img width="1223" alt="Screenshot 2024-12-11 at 8 21 12 PM" src="https://github.com/user-attachments/assets/a7cbc04b-c831-476b-aa2f-baddec9461c9" /> This emits a placeholder when we're deduping a component. This starts when the parent's self time ends, where we would've started rendering this component if it wasn't already started. The end time is when the actual render ends since the parent is also blocked by it.
1 parent 07facb5 commit bdf1871

File tree

3 files changed

+60
-6
lines changed

3 files changed

+60
-6
lines changed

fixtures/flight/src/App.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ function Foo({children}) {
2727
return <div>{children}</div>;
2828
}
2929

30-
function Bar({children}) {
30+
async function Bar({children}) {
31+
await new Promise(resolve => setTimeout(() => resolve('deferred text'), 10));
3132
return <div>{children}</div>;
3233
}
3334

@@ -81,7 +82,7 @@ export default async function App({prerender}) {
8182
<Client />
8283
<Note />
8384
<Foo>{dedupedChild}</Foo>
84-
<Bar>{dedupedChild}</Bar>
85+
<Bar>{Promise.resolve([dedupedChild])}</Bar>
8586
</Container>
8687
</body>
8788
</html>

packages/react-client/src/ReactFlightClient.js

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import {readTemporaryReference} from './ReactFlightTemporaryReferences';
7272
import {
7373
markAllTracksInOrder,
7474
logComponentRender,
75+
logDedupedComponentRender,
7576
} from './ReactFlightPerformanceTrack';
7677

7778
import {
@@ -130,6 +131,7 @@ export type JSONValue =
130131
type ProfilingResult = {
131132
track: number,
132133
endTime: number,
134+
component: null | ReactComponentInfo,
133135
};
134136

135137
const ROW_ID = 0;
@@ -647,7 +649,7 @@ export function reportGlobalError(response: Response, error: Error): void {
647649
});
648650
if (enableProfilerTimer && enableComponentPerformanceTrack) {
649651
markAllTracksInOrder();
650-
flushComponentPerformance(getChunk(response, 0), 0, -Infinity);
652+
flushComponentPerformance(getChunk(response, 0), 0, -Infinity, -Infinity);
651653
}
652654
}
653655

@@ -2748,7 +2750,8 @@ function resolveTypedArray(
27482750
function flushComponentPerformance(
27492751
root: SomeChunk<any>,
27502752
trackIdx: number, // Next available track
2751-
trackTime: number, // The time after which it is available
2753+
trackTime: number, // The time after which it is available,
2754+
parentEndTime: number,
27522755
): ProfilingResult {
27532756
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
27542757
// eslint-disable-next-line react-internal/prod-error-codes
@@ -2765,6 +2768,22 @@ function flushComponentPerformance(
27652768
// chunk in two places. We should extend the current end time as if it was
27662769
// rendered as part of this tree.
27672770
const previousResult: ProfilingResult = root._children;
2771+
const previousEndTime = previousResult.endTime;
2772+
if (
2773+
parentEndTime > -Infinity &&
2774+
parentEndTime < previousEndTime &&
2775+
previousResult.component !== null
2776+
) {
2777+
// Log a placeholder for the deduped value under this child starting
2778+
// from the end of the self time of the parent and spanning until the
2779+
// the deduped end.
2780+
logDedupedComponentRender(
2781+
previousResult.component,
2782+
trackIdx,
2783+
parentEndTime,
2784+
previousEndTime,
2785+
);
2786+
}
27682787
// Since we didn't bump the track this time, we just return the same track.
27692788
previousResult.track = trackIdx;
27702789
return previousResult;
@@ -2792,15 +2811,27 @@ function flushComponentPerformance(
27922811
// The start time of this component is before the end time of the previous
27932812
// component on this track so we need to bump the next one to a parallel track.
27942813
trackIdx++;
2795-
trackTime = startTime;
27962814
}
2815+
trackTime = startTime;
27972816
break;
27982817
}
27992818
}
28002819
}
2820+
for (let i = debugInfo.length - 1; i >= 0; i--) {
2821+
const info = debugInfo[i];
2822+
if (typeof info.time === 'number') {
2823+
if (info.time > parentEndTime) {
2824+
parentEndTime = info.time;
2825+
}
2826+
}
2827+
}
28012828
}
28022829

2803-
const result: ProfilingResult = {track: trackIdx, endTime: -Infinity};
2830+
const result: ProfilingResult = {
2831+
track: trackIdx,
2832+
endTime: -Infinity,
2833+
component: null,
2834+
};
28042835
root._children = result;
28052836
let childrenEndTime = -Infinity;
28062837
let childTrackIdx = trackIdx;
@@ -2810,7 +2841,11 @@ function flushComponentPerformance(
28102841
children[i],
28112842
childTrackIdx,
28122843
childTrackTime,
2844+
parentEndTime,
28132845
);
2846+
if (childResult.component !== null) {
2847+
result.component = childResult.component;
2848+
}
28142849
childTrackIdx = childResult.track;
28152850
const childEndTime = childResult.endTime;
28162851
childTrackTime = childEndTime;
@@ -2842,6 +2877,8 @@ function flushComponentPerformance(
28422877
endTime,
28432878
childrenEndTime,
28442879
);
2880+
// Track the root most component of the result for deduping logging.
2881+
result.component = componentInfo;
28452882
}
28462883
}
28472884
}

packages/react-client/src/ReactFlightPerformanceTrack.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,19 @@ export function logComponentRender(
9090
performance.measure(name, reusableComponentOptions);
9191
}
9292
}
93+
94+
export function logDedupedComponentRender(
95+
componentInfo: ReactComponentInfo,
96+
trackIdx: number,
97+
startTime: number,
98+
endTime: number,
99+
): void {
100+
if (supportsUserTiming && endTime >= 0 && trackIdx < 10) {
101+
const name = componentInfo.name;
102+
reusableComponentDevToolDetails.color = 'tertiary-light';
103+
reusableComponentDevToolDetails.track = trackNames[trackIdx];
104+
reusableComponentOptions.start = startTime < 0 ? 0 : startTime;
105+
reusableComponentOptions.end = endTime;
106+
performance.measure(name + ' [deduped]', reusableComponentOptions);
107+
}
108+
}

0 commit comments

Comments
 (0)