Skip to content

Commit 012b371

Browse files
authored
[DevTools] Handle LegacyHidden Fibers like Offscreen Fibers. (#34564)
1 parent 83c88ad commit 012b371

File tree

4 files changed

+45
-74
lines changed

4 files changed

+45
-74
lines changed

packages/react-devtools-shared/src/__tests__/profilingCache-test.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -725,14 +725,14 @@ describe('ProfilingCache', () => {
725725
const commitData = store.profilerStore.getDataForRoot(rootID).commitData;
726726
expect(commitData).toHaveLength(2);
727727

728-
const isLegacySuspense = React.version.startsWith('17');
729-
if (isLegacySuspense) {
728+
if (React.version.startsWith('17')) {
729+
// React 17 will mount all children until it suspends in a LegacyHidden
730+
// The ID gap is from the Fiber for <Async> that's in the disconnected tree.
730731
expect(commitData[0].fiberActualDurations).toMatchInlineSnapshot(`
731732
Map {
732733
1 => 15,
733734
2 => 15,
734735
3 => 5,
735-
4 => 3,
736736
5 => 2,
737737
}
738738
`);
@@ -741,7 +741,6 @@ describe('ProfilingCache', () => {
741741
1 => 0,
742742
2 => 10,
743743
3 => 3,
744-
4 => 3,
745744
5 => 2,
746745
}
747746
`);

packages/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder-test.js

Lines changed: 16 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ describe('commit tree', () => {
1919
let Scheduler;
2020
let store: Store;
2121
let utils;
22-
const isLegacySuspense =
23-
React.version.startsWith('16') || React.version.startsWith('17');
2422

2523
beforeEach(() => {
2624
utils = require('./utils');
@@ -186,24 +184,13 @@ describe('commit tree', () => {
186184
utils.act(() => store.profilerStore.startProfiling());
187185
utils.act(() => legacyRender(<App renderChildren={true} />));
188186
await Promise.resolve();
189-
if (isLegacySuspense) {
190-
expect(store).toMatchInlineSnapshot(`
191-
[root]
192-
▾ <App>
193-
▾ <Suspense>
194-
<Lazy>
195-
[suspense-root] rects={null}
196-
<Suspense name="App" rects={null}>
197-
`);
198-
} else {
199-
expect(store).toMatchInlineSnapshot(`
200-
[root]
201-
▾ <App>
202-
<Suspense>
203-
[suspense-root] rects={null}
204-
<Suspense name="App" rects={null}>
205-
`);
206-
}
187+
expect(store).toMatchInlineSnapshot(`
188+
[root]
189+
▾ <App>
190+
<Suspense>
191+
[suspense-root] rects={null}
192+
<Suspense name="App" rects={null}>
193+
`);
207194
utils.act(() => legacyRender(<App renderChildren={true} />));
208195
expect(store).toMatchInlineSnapshot(`
209196
[root]
@@ -231,13 +218,7 @@ describe('commit tree', () => {
231218
);
232219
}
233220

234-
expect(commitTrees[0].nodes.size).toBe(
235-
isLegacySuspense
236-
? // <Root> + <App> + <Suspense> + <Lazy>
237-
4
238-
: // <Root> + <App> + <Suspense>
239-
3,
240-
);
221+
expect(commitTrees[0].nodes.size).toBe(3);
241222
expect(commitTrees[1].nodes.size).toBe(4); // <Root> + <App> + <Suspense> + <LazyInnerComponent>
242223
expect(commitTrees[2].nodes.size).toBe(2); // <Root> + <App>
243224
});
@@ -291,24 +272,13 @@ describe('commit tree', () => {
291272
it('should support Lazy components that are unmounted before resolving (legacy render)', async () => {
292273
utils.act(() => store.profilerStore.startProfiling());
293274
utils.act(() => legacyRender(<App renderChildren={true} />));
294-
if (isLegacySuspense) {
295-
expect(store).toMatchInlineSnapshot(`
296-
[root]
297-
▾ <App>
298-
▾ <Suspense>
299-
<Lazy>
300-
[suspense-root] rects={null}
301-
<Suspense name="App" rects={null}>
302-
`);
303-
} else {
304-
expect(store).toMatchInlineSnapshot(`
305-
[root]
306-
▾ <App>
307-
<Suspense>
308-
[suspense-root] rects={null}
309-
<Suspense name="App" rects={null}>
310-
`);
311-
}
275+
expect(store).toMatchInlineSnapshot(`
276+
[root]
277+
▾ <App>
278+
<Suspense>
279+
[suspense-root] rects={null}
280+
<Suspense name="App" rects={null}>
281+
`);
312282
utils.act(() => legacyRender(<App renderChildren={false} />));
313283
expect(store).toMatchInlineSnapshot(`
314284
[root]
@@ -327,13 +297,7 @@ describe('commit tree', () => {
327297
);
328298
}
329299

330-
expect(commitTrees[0].nodes.size).toBe(
331-
isLegacySuspense
332-
? // <Root> + <App> + <Suspense> + <Lazy>
333-
4
334-
: // <Root> + <App> + <Suspense>
335-
3,
336-
);
300+
expect(commitTrees[0].nodes.size).toBe(3);
337301
expect(commitTrees[1].nodes.size).toBe(2); // <Root> + <App>
338302
});
339303

packages/react-devtools-shared/src/__tests__/store-test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2828,7 +2828,7 @@ describe('Store', () => {
28282828
`);
28292829
});
28302830

2831-
// @reactVersion >= 18.0
2831+
// @reactVersion >= 17.0
28322832
it('can reconcile Suspense in fallback positions', async () => {
28332833
let resolveFallback;
28342834
const fallbackPromise = new Promise(resolve => {
@@ -2907,7 +2907,7 @@ describe('Store', () => {
29072907
`);
29082908
});
29092909

2910-
// @reactVersion >= 18.0
2910+
// @reactVersion >= 17.0
29112911
it('can reconcile resuspended Suspense with Suspense in fallback positions', async () => {
29122912
let resolveHeadFallback;
29132913
let resolveHeadContent;

packages/react-devtools-shared/src/backend/fiber/renderer.js

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,10 @@ export function getInternalReactConstants(version: string): {
460460
IncompleteFunctionComponent: 28,
461461
IndeterminateComponent: 2, // removed in 19.0.0
462462
LazyComponent: 16,
463-
LegacyHiddenComponent: 23,
463+
LegacyHiddenComponent: 23, // Does not exist in 18+ OSS but exists in fb builds
464464
MemoComponent: 14,
465465
Mode: 8,
466-
OffscreenComponent: 22, // Experimental
466+
OffscreenComponent: 22, // Experimental in 17. Stable in 18+
467467
Profiler: 12,
468468
ScopeComponent: 21, // Experimental
469469
SimpleMemoComponent: 15,
@@ -3057,13 +3057,23 @@ export function attach(
30573057
}
30583058
}
30593059
3060+
function isHiddenOffscreen(fiber: Fiber): boolean {
3061+
switch (fiber.tag) {
3062+
case LegacyHiddenComponent:
3063+
// fallthrough since all published implementations currently implement the same state as Offscreen.
3064+
case OffscreenComponent:
3065+
return fiber.memoizedState !== null;
3066+
default:
3067+
return false;
3068+
}
3069+
}
3070+
30603071
function unmountRemainingChildren() {
30613072
if (
30623073
reconcilingParent !== null &&
30633074
(reconcilingParent.kind === FIBER_INSTANCE ||
30643075
reconcilingParent.kind === FILTERED_FIBER_INSTANCE) &&
3065-
reconcilingParent.data.tag === OffscreenComponent &&
3066-
reconcilingParent.data.memoizedState !== null &&
3076+
isHiddenOffscreen(reconcilingParent.data) &&
30673077
!isInDisconnectedSubtree
30683078
) {
30693079
// This is a hidden offscreen, we need to execute this in the context of a disconnected subtree.
@@ -3170,8 +3180,7 @@ export function attach(
31703180
if (
31713181
(parent.kind === FIBER_INSTANCE ||
31723182
parent.kind === FILTERED_FIBER_INSTANCE) &&
3173-
parent.data.tag === OffscreenComponent &&
3174-
parent.data.memoizedState !== null
3183+
isHiddenOffscreen(parent.data)
31753184
) {
31763185
// We're inside a hidden offscreen Fiber. We're in a disconnected tree.
31773186
return;
@@ -3819,7 +3828,9 @@ export function attach(
38193828
(reconcilingParent !== null &&
38203829
reconcilingParent.kind === VIRTUAL_INSTANCE) ||
38213830
fiber.tag === SuspenseComponent ||
3822-
fiber.tag === OffscreenComponent // Use to keep resuspended instances alive inside a SuspenseComponent.
3831+
// Use to keep resuspended instances alive inside a SuspenseComponent.
3832+
fiber.tag === OffscreenComponent ||
3833+
fiber.tag === LegacyHiddenComponent
38233834
) {
38243835
// If the parent is a Virtual Instance and we filtered this Fiber we include a
38253836
// hidden node. We also include this if it's a Suspense boundary so we can track those
@@ -3939,7 +3950,7 @@ export function attach(
39393950
trackDebugInfoFromHostComponent(nearestInstance, fiber);
39403951
}
39413952
3942-
if (fiber.tag === OffscreenComponent && fiber.memoizedState !== null) {
3953+
if (isHiddenOffscreen(fiber)) {
39433954
// If an Offscreen component is hidden, mount its children as disconnected.
39443955
const stashedDisconnected = isInDisconnectedSubtree;
39453956
isInDisconnectedSubtree = true;
@@ -4261,7 +4272,7 @@ export function attach(
42614272
while (child !== null) {
42624273
if (child.kind === FILTERED_FIBER_INSTANCE) {
42634274
const fiber = child.data;
4264-
if (fiber.tag === OffscreenComponent && fiber.memoizedState !== null) {
4275+
if (isHiddenOffscreen(fiber)) {
42654276
// The children of this Offscreen are hidden so they don't get added.
42664277
} else {
42674278
addUnfilteredChildrenIDs(child, nextChildren);
@@ -4888,9 +4899,8 @@ export function attach(
48884899
const nextDidTimeOut =
48894900
isLegacySuspense && nextFiber.memoizedState !== null;
48904901
4891-
const isOffscreen = nextFiber.tag === OffscreenComponent;
4892-
const prevWasHidden = isOffscreen && prevFiber.memoizedState !== null;
4893-
const nextIsHidden = isOffscreen && nextFiber.memoizedState !== null;
4902+
const prevWasHidden = isHiddenOffscreen(prevFiber);
4903+
const nextIsHidden = isHiddenOffscreen(nextFiber);
48944904
48954905
if (isLegacySuspense) {
48964906
if (
@@ -5245,8 +5255,7 @@ export function attach(
52455255
if (
52465256
(child.kind === FIBER_INSTANCE ||
52475257
child.kind === FILTERED_FIBER_INSTANCE) &&
5248-
child.data.tag === OffscreenComponent &&
5249-
child.data.memoizedState !== null
5258+
isHiddenOffscreen(child.data)
52505259
) {
52515260
// This instance's children are already disconnected.
52525261
} else {
@@ -5275,8 +5284,7 @@ export function attach(
52755284
if (
52765285
(child.kind === FIBER_INSTANCE ||
52775286
child.kind === FILTERED_FIBER_INSTANCE) &&
5278-
child.data.tag === OffscreenComponent &&
5279-
child.data.memoizedState !== null
5287+
isHiddenOffscreen(child.data)
52805288
) {
52815289
// This instance's children should remain disconnected.
52825290
} else {

0 commit comments

Comments
 (0)