Skip to content

Commit f493a42

Browse files
committed
[DevTools] Handle LegacyHidden Fibers like Offscreen Fibers.
This is mostly for React 17 which uses `LegacyHiddenComponent` instead of `Offscreen` for the content Fiber.
1 parent cad813a commit f493a42

File tree

4 files changed

+71
-132
lines changed

4 files changed

+71
-132
lines changed

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

Lines changed: 28 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -725,68 +725,34 @@ 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) {
730-
expect(commitData[0].fiberActualDurations).toMatchInlineSnapshot(`
731-
Map {
732-
1 => 15,
733-
2 => 15,
734-
3 => 5,
735-
4 => 3,
736-
5 => 2,
737-
}
738-
`);
739-
expect(commitData[0].fiberSelfDurations).toMatchInlineSnapshot(`
740-
Map {
741-
1 => 0,
742-
2 => 10,
743-
3 => 3,
744-
4 => 3,
745-
5 => 2,
746-
}
747-
`);
748-
expect(commitData[1].fiberActualDurations).toMatchInlineSnapshot(`
749-
Map {
750-
6 => 3,
751-
3 => 3,
752-
}
753-
`);
754-
expect(commitData[1].fiberSelfDurations).toMatchInlineSnapshot(`
755-
Map {
756-
6 => 3,
757-
3 => 0,
758-
}
759-
`);
760-
} else {
761-
expect(commitData[0].fiberActualDurations).toMatchInlineSnapshot(`
762-
Map {
763-
1 => 15,
764-
2 => 15,
765-
3 => 5,
766-
4 => 2,
767-
}
768-
`);
769-
expect(commitData[0].fiberSelfDurations).toMatchInlineSnapshot(`
770-
Map {
771-
1 => 0,
772-
2 => 10,
773-
3 => 3,
774-
4 => 2,
775-
}
776-
`);
777-
expect(commitData[1].fiberActualDurations).toMatchInlineSnapshot(`
778-
Map {
779-
5 => 3,
780-
3 => 3,
781-
}
782-
`);
783-
expect(commitData[1].fiberSelfDurations).toMatchInlineSnapshot(`
784-
Map {
785-
5 => 3,
786-
3 => 0,
787-
}
788-
`);
789-
}
728+
expect(commitData[0].fiberActualDurations).toMatchInlineSnapshot(`
729+
Map {
730+
1 => 15,
731+
2 => 15,
732+
3 => 5,
733+
5 => 2,
734+
}
735+
`);
736+
expect(commitData[0].fiberSelfDurations).toMatchInlineSnapshot(`
737+
Map {
738+
1 => 0,
739+
2 => 10,
740+
3 => 3,
741+
5 => 2,
742+
}
743+
`);
744+
expect(commitData[1].fiberActualDurations).toMatchInlineSnapshot(`
745+
Map {
746+
6 => 3,
747+
3 => 3,
748+
}
749+
`);
750+
expect(commitData[1].fiberSelfDurations).toMatchInlineSnapshot(`
751+
Map {
752+
6 => 3,
753+
3 => 0,
754+
}
755+
`);
790756
});
791757

792758
// @reactVersion >= 16.9

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: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,11 @@ 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 - This is technically in 18 but we don't
467+
// want to fork again so we're adding it here instead
467468
Profiler: 12,
468469
ScopeComponent: 21, // Experimental
469470
SimpleMemoComponent: 15,
@@ -3057,13 +3058,23 @@ export function attach(
30573058
}
30583059
}
30593060
3061+
function isHiddenOffscreen(fiber: Fiber): boolean {
3062+
switch (fiber.tag) {
3063+
case LegacyHiddenComponent:
3064+
// fallthrough since all published implementations currently implement the same state as Offscreen.
3065+
case OffscreenComponent:
3066+
return fiber.memoizedState !== null;
3067+
default:
3068+
return false;
3069+
}
3070+
}
3071+
30603072
function unmountRemainingChildren() {
30613073
if (
30623074
reconcilingParent !== null &&
30633075
(reconcilingParent.kind === FIBER_INSTANCE ||
30643076
reconcilingParent.kind === FILTERED_FIBER_INSTANCE) &&
3065-
reconcilingParent.data.tag === OffscreenComponent &&
3066-
reconcilingParent.data.memoizedState !== null &&
3077+
isHiddenOffscreen(reconcilingParent.data) &&
30673078
!isInDisconnectedSubtree
30683079
) {
30693080
// This is a hidden offscreen, we need to execute this in the context of a disconnected subtree.
@@ -3170,8 +3181,7 @@ export function attach(
31703181
if (
31713182
(parent.kind === FIBER_INSTANCE ||
31723183
parent.kind === FILTERED_FIBER_INSTANCE) &&
3173-
parent.data.tag === OffscreenComponent &&
3174-
parent.data.memoizedState !== null
3184+
isHiddenOffscreen(parent.data)
31753185
) {
31763186
// We're inside a hidden offscreen Fiber. We're in a disconnected tree.
31773187
return;
@@ -3819,7 +3829,9 @@ export function attach(
38193829
(reconcilingParent !== null &&
38203830
reconcilingParent.kind === VIRTUAL_INSTANCE) ||
38213831
fiber.tag === SuspenseComponent ||
3822-
fiber.tag === OffscreenComponent // Use to keep resuspended instances alive inside a SuspenseComponent.
3832+
// Use to keep resuspended instances alive inside a SuspenseComponent.
3833+
fiber.tag === OffscreenComponent ||
3834+
fiber.tag === LegacyHiddenComponent
38233835
) {
38243836
// If the parent is a Virtual Instance and we filtered this Fiber we include a
38253837
// hidden node. We also include this if it's a Suspense boundary so we can track those
@@ -3939,7 +3951,7 @@ export function attach(
39393951
trackDebugInfoFromHostComponent(nearestInstance, fiber);
39403952
}
39413953
3942-
if (fiber.tag === OffscreenComponent && fiber.memoizedState !== null) {
3954+
if (isHiddenOffscreen(fiber)) {
39433955
// If an Offscreen component is hidden, mount its children as disconnected.
39443956
const stashedDisconnected = isInDisconnectedSubtree;
39453957
isInDisconnectedSubtree = true;
@@ -4261,7 +4273,7 @@ export function attach(
42614273
while (child !== null) {
42624274
if (child.kind === FILTERED_FIBER_INSTANCE) {
42634275
const fiber = child.data;
4264-
if (fiber.tag === OffscreenComponent && fiber.memoizedState !== null) {
4276+
if (isHiddenOffscreen(fiber)) {
42654277
// The children of this Offscreen are hidden so they don't get added.
42664278
} else {
42674279
addUnfilteredChildrenIDs(child, nextChildren);
@@ -4888,9 +4900,8 @@ export function attach(
48884900
const nextDidTimeOut =
48894901
isLegacySuspense && nextFiber.memoizedState !== null;
48904902
4891-
const isOffscreen = nextFiber.tag === OffscreenComponent;
4892-
const prevWasHidden = isOffscreen && prevFiber.memoizedState !== null;
4893-
const nextIsHidden = isOffscreen && nextFiber.memoizedState !== null;
4903+
const prevWasHidden = isHiddenOffscreen(prevFiber);
4904+
const nextIsHidden = isHiddenOffscreen(nextFiber);
48944905
48954906
if (isLegacySuspense) {
48964907
if (
@@ -5245,8 +5256,7 @@ export function attach(
52455256
if (
52465257
(child.kind === FIBER_INSTANCE ||
52475258
child.kind === FILTERED_FIBER_INSTANCE) &&
5248-
child.data.tag === OffscreenComponent &&
5249-
child.data.memoizedState !== null
5259+
isHiddenOffscreen(child.data)
52505260
) {
52515261
// This instance's children are already disconnected.
52525262
} else {
@@ -5275,8 +5285,7 @@ export function attach(
52755285
if (
52765286
(child.kind === FIBER_INSTANCE ||
52775287
child.kind === FILTERED_FIBER_INSTANCE) &&
5278-
child.data.tag === OffscreenComponent &&
5279-
child.data.memoizedState !== null
5288+
isHiddenOffscreen(child.data)
52805289
) {
52815290
// This instance's children should remain disconnected.
52825291
} else {

0 commit comments

Comments
 (0)