Skip to content

Commit b037f03

Browse files
committed
Filtered Fiber Instances
1 parent d1afcb4 commit b037f03

File tree

1 file changed

+116
-27
lines changed
  • packages/react-devtools-shared/src/backend/fiber

1 file changed

+116
-27
lines changed

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

+116-27
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ import {formatOwnerStack} from '../shared/DevToolsOwnerStack';
146146
// Kinds
147147
const FIBER_INSTANCE = 0;
148148
const VIRTUAL_INSTANCE = 1;
149+
const FILTERED_FIBER_INSTANCE = 2;
149150

150151
// Flags
151152
const FORCE_SUSPENSE_FALLBACK = /* */ 0b001;
@@ -157,9 +158,9 @@ const FORCE_ERROR_RESET = /* */ 0b100;
157158
type FiberInstance = {
158159
kind: 0,
159160
id: number,
160-
parent: null | DevToolsInstance, // filtered parent, including virtual
161-
firstChild: null | DevToolsInstance, // filtered first child, including virtual
162-
nextSibling: null | DevToolsInstance, // filtered next sibling, including virtual
161+
parent: null | DevToolsInstance,
162+
firstChild: null | DevToolsInstance,
163+
nextSibling: null | DevToolsInstance,
163164
flags: number, // Force Error/Suspense
164165
source: null | string | Error | Source, // source location of this component function, or owned child stack
165166
errors: null | Map<string, number>, // error messages and count
@@ -184,6 +185,39 @@ function createFiberInstance(fiber: Fiber): FiberInstance {
184185
};
185186
}
186187

188+
type FilteredFiberInstance = {
189+
kind: 2,
190+
// We exclude id from the type to get errors if we try to access it.
191+
// However it is still in the object to preserve hidden class.
192+
// id: number,
193+
parent: null | DevToolsInstance,
194+
firstChild: null | DevToolsInstance,
195+
nextSibling: null | DevToolsInstance,
196+
flags: number, // Force Error/Suspense
197+
source: null | string | Error | Source, // always null here.
198+
errors: null, // error messages and count
199+
warnings: null, // warning messages and count
200+
treeBaseDuration: number, // the profiled time of the last render of this subtree
201+
data: Fiber, // one of a Fiber pair
202+
};
203+
204+
// This is used to represent a filtered Fiber but still lets us find its host instance.
205+
function createFilteredFiberInstance(fiber: Fiber): FilteredFiberInstance {
206+
return ({
207+
kind: FILTERED_FIBER_INSTANCE,
208+
id: 0,
209+
parent: null,
210+
firstChild: null,
211+
nextSibling: null,
212+
flags: 0,
213+
componentStack: null,
214+
errors: null,
215+
warnings: null,
216+
treeBaseDuration: 0,
217+
data: fiber,
218+
}: any);
219+
}
220+
187221
// This type represents a stateful instance of a Server Component or a Component
188222
// that gets optimized away - e.g. call-through without creating a Fiber.
189223
// It's basically a virtual Fiber. This is not a semantic concept in React.
@@ -192,9 +226,9 @@ function createFiberInstance(fiber: Fiber): FiberInstance {
192226
type VirtualInstance = {
193227
kind: 1,
194228
id: number,
195-
parent: null | DevToolsInstance, // filtered parent, including virtual
196-
firstChild: null | DevToolsInstance, // filtered first child, including virtual
197-
nextSibling: null | DevToolsInstance, // filtered next sibling, including virtual
229+
parent: null | DevToolsInstance,
230+
firstChild: null | DevToolsInstance,
231+
nextSibling: null | DevToolsInstance,
198232
flags: number,
199233
source: null | string | Error | Source, // source location of this server component, or owned child stack
200234
// Errors and Warnings happen per ReactComponentInfo which can appear in
@@ -226,7 +260,7 @@ function createVirtualInstance(
226260
};
227261
}
228262

229-
type DevToolsInstance = FiberInstance | VirtualInstance;
263+
type DevToolsInstance = FiberInstance | VirtualInstance | FilteredFiberInstance;
230264

231265
type getDisplayNameForFiberType = (fiber: Fiber) => string | null;
232266
type getTypeSymbolType = (type: any) => symbol | number;
@@ -736,7 +770,8 @@ const fiberToFiberInstanceMap: Map<Fiber, FiberInstance> = new Map();
736770
// Map of id to one (arbitrary) Fiber in a pair.
737771
// This Map is used to e.g. get the display name for a Fiber or schedule an update,
738772
// operations that should be the same whether the current and work-in-progress Fiber is used.
739-
const idToDevToolsInstanceMap: Map<number, DevToolsInstance> = new Map();
773+
const idToDevToolsInstanceMap: Map<number, FiberInstance | VirtualInstance> =
774+
new Map();
740775

741776
// Map of canonical HostInstances to the nearest parent DevToolsInstance.
742777
const publicInstanceToDevToolsInstanceMap: Map<HostInstance, DevToolsInstance> =
@@ -1141,13 +1176,22 @@ export function attach(
11411176
function debugTree(instance: DevToolsInstance, indent: number = 0) {
11421177
if (__DEBUG__) {
11431178
const name =
1144-
(instance.kind === FIBER_INSTANCE
1179+
(instance.kind !== VIRTUAL_INSTANCE
11451180
? getDisplayNameForFiber(instance.data)
11461181
: instance.data.name) || '';
11471182
console.log(
1148-
' '.repeat(indent) + '- ' + instance.id + ' (' + name + ')',
1183+
' '.repeat(indent) +
1184+
'- ' +
1185+
(instance.kind === FILTERED_FIBER_INSTANCE ? 0 : instance.id) +
1186+
' (' +
1187+
name +
1188+
')',
11491189
'parent',
1150-
instance.parent === null ? ' ' : instance.parent.id,
1190+
instance.parent === null
1191+
? ' '
1192+
: instance.parent.kind === FILTERED_FIBER_INSTANCE
1193+
? 0
1194+
: instance.parent.id,
11511195
'next',
11521196
instance.nextSibling === null ? ' ' : instance.nextSibling.id,
11531197
);
@@ -2264,7 +2308,12 @@ export function attach(
22642308
ownerInstance.source = fiber._debugStack;
22652309
}
22662310
const ownerID = ownerInstance === null ? 0 : ownerInstance.id;
2267-
const parentID = parentInstance ? parentInstance.id : 0;
2311+
const parentID = parentInstance
2312+
? parentInstance.kind === FILTERED_FIBER_INSTANCE
2313+
? // A Filtered Fiber Instance will always have a Virtual Instance as a parent.
2314+
((parentInstance.parent: any): VirtualInstance).id
2315+
: parentInstance.id
2316+
: 0;
22682317

22692318
const displayNameStringID = getStringID(displayName);
22702319

@@ -2348,7 +2397,12 @@ export function attach(
23482397
ownerInstance.source = componentInfo.debugStack;
23492398
}
23502399
const ownerID = ownerInstance === null ? 0 : ownerInstance.id;
2351-
const parentID = parentInstance ? parentInstance.id : 0;
2400+
const parentID = parentInstance
2401+
? parentInstance.kind === FILTERED_FIBER_INSTANCE
2402+
? // A Filtered Fiber Instance will always have a Virtual Instance as a parent.
2403+
((parentInstance.parent: any): VirtualInstance).id
2404+
: parentInstance.id
2405+
: 0;
23522406

23532407
const displayNameStringID = getStringID(displayName);
23542408

@@ -2713,6 +2767,14 @@ export function attach(
27132767
if (shouldIncludeInTree) {
27142768
newInstance = recordMount(fiber, reconcilingParent);
27152769
insertChild(newInstance);
2770+
} else if (
2771+
reconcilingParent !== null &&
2772+
reconcilingParent.kind === VIRTUAL_INSTANCE
2773+
) {
2774+
// If the parent is a Virtual Instance and we filtered this Fiber we include a
2775+
// hidden node.
2776+
newInstance = createFilteredFiberInstance(fiber);
2777+
insertChild(newInstance);
27162778
}
27172779

27182780
// If we have the tree selection from previous reload, try to match this Fiber.
@@ -2725,7 +2787,7 @@ export function attach(
27252787
const stashedParent = reconcilingParent;
27262788
const stashedPrevious = previouslyReconciledSibling;
27272789
const stashedRemaining = remainingReconcilingChildren;
2728-
if (shouldIncludeInTree) {
2790+
if (newInstance !== null) {
27292791
// Push a new DevTools instance parent while reconciling this subtree.
27302792
reconcilingParent = newInstance;
27312793
previouslyReconciledSibling = null;
@@ -2810,7 +2872,7 @@ export function attach(
28102872
}
28112873
}
28122874
} finally {
2813-
if (shouldIncludeInTree) {
2875+
if (newInstance !== null) {
28142876
reconcilingParent = stashedParent;
28152877
previouslyReconciledSibling = stashedPrevious;
28162878
remainingReconcilingChildren = stashedRemaining;
@@ -2850,8 +2912,10 @@ export function attach(
28502912
}
28512913
if (instance.kind === FIBER_INSTANCE) {
28522914
recordUnmount(instance);
2853-
} else {
2915+
} else if (instance.kind === VIRTUAL_INSTANCE) {
28542916
recordVirtualUnmount(instance);
2917+
} else {
2918+
untrackFiber(instance, instance.data);
28552919
}
28562920
removeChild(instance, null);
28572921
}
@@ -2956,7 +3020,9 @@ export function attach(
29563020
virtualInstance.treeBaseDuration = treeBaseDuration;
29573021
}
29583022

2959-
function recordResetChildren(parentInstance: DevToolsInstance) {
3023+
function recordResetChildren(
3024+
parentInstance: FiberInstance | VirtualInstance,
3025+
) {
29603026
if (__DEBUG__) {
29613027
if (
29623028
parentInstance.firstChild !== null &&
@@ -2976,7 +3042,17 @@ export function attach(
29763042

29773043
let child: null | DevToolsInstance = parentInstance.firstChild;
29783044
while (child !== null) {
2979-
nextChildren.push(child.id);
3045+
if (child.kind === FILTERED_FIBER_INSTANCE) {
3046+
for (
3047+
let innerChild: null | DevToolsInstance = parentInstance.firstChild;
3048+
innerChild !== null;
3049+
innerChild = innerChild.nextSibling
3050+
) {
3051+
nextChildren.push((innerChild: any).id);
3052+
}
3053+
} else {
3054+
nextChildren.push(child.id);
3055+
}
29803056
child = child.nextSibling;
29813057
}
29823058

@@ -3788,7 +3864,7 @@ export function attach(
37883864
devtoolsInstance: DevToolsInstance,
37893865
hostInstances: Array<HostInstance>,
37903866
) {
3791-
if (devtoolsInstance.kind === FIBER_INSTANCE) {
3867+
if (devtoolsInstance.kind !== VIRTUAL_INSTANCE) {
37923868
const fiber = devtoolsInstance.data;
37933869
appendHostInstancesByFiber(fiber, hostInstances);
37943870
return;
@@ -3889,6 +3965,10 @@ export function attach(
38893965
): number | null {
38903966
const instance = publicInstanceToDevToolsInstanceMap.get(publicInstance);
38913967
if (instance !== undefined) {
3968+
if (instance.kind === FILTERED_FIBER_INSTANCE) {
3969+
// A Filtered Fiber Instance will always have a Virtual Instance as a parent.
3970+
return ((instance.parent: any): VirtualInstance).id;
3971+
}
38923972
return instance.id;
38933973
}
38943974
return null;
@@ -3941,7 +4021,7 @@ export function attach(
39414021
}
39424022

39434023
function instanceToSerializedElement(
3944-
instance: DevToolsInstance,
4024+
instance: FiberInstance | VirtualInstance,
39454025
): SerializedElement {
39464026
if (instance.kind === FIBER_INSTANCE) {
39474027
const fiber = instance.data;
@@ -4036,7 +4116,7 @@ export function attach(
40364116
function findNearestOwnerInstance(
40374117
parentInstance: null | DevToolsInstance,
40384118
owner: void | null | ReactComponentInfo | Fiber,
4039-
): null | DevToolsInstance {
4119+
): null | FiberInstance | VirtualInstance {
40404120
if (owner == null) {
40414121
return null;
40424122
}
@@ -4051,6 +4131,9 @@ export function attach(
40514131
// needs a duck type check anyway.
40524132
parentInstance.data === (owner: any).alternate
40534133
) {
4134+
if (parentInstance.kind === FILTERED_FIBER_INSTANCE) {
4135+
return null;
4136+
}
40544137
return parentInstance;
40554138
}
40564139
parentInstance = parentInstance.parent;
@@ -4128,7 +4211,11 @@ export function attach(
41284211
if (devtoolsInstance.kind === VIRTUAL_INSTANCE) {
41294212
return inspectVirtualInstanceRaw(devtoolsInstance);
41304213
}
4131-
return inspectFiberInstanceRaw(devtoolsInstance);
4214+
if (devtoolsInstance.kind === FIBER_INSTANCE) {
4215+
return inspectFiberInstanceRaw(devtoolsInstance);
4216+
}
4217+
(devtoolsInstance: FilteredFiberInstance); // assert exhaustive
4218+
throw new Error('Unsupported instance kind');
41324219
}
41334220

41344221
function inspectFiberInstanceRaw(
@@ -4431,7 +4518,7 @@ export function attach(
44314518
let targetErrorBoundaryID = null;
44324519
let parent = virtualInstance.parent;
44334520
while (parent !== null) {
4434-
if (parent.kind === FIBER_INSTANCE) {
4521+
if (parent.kind !== VIRTUAL_INSTANCE) {
44354522
targetErrorBoundaryID = getNearestErrorBoundaryID(parent.data);
44364523
let current = parent.data;
44374524
while (current.return !== null) {
@@ -5222,7 +5309,9 @@ export function attach(
52225309
) {
52235310
// We don't need to convert milliseconds to microseconds in this case,
52245311
// because the profiling summary is JSON serialized.
5225-
target.push([instance.id, instance.treeBaseDuration]);
5312+
if (instance.kind !== FILTERED_FIBER_INSTANCE) {
5313+
target.push([instance.id, instance.treeBaseDuration]);
5314+
}
52265315
for (
52275316
let child = instance.firstChild;
52285317
child !== null;
@@ -5436,7 +5525,7 @@ export function attach(
54365525
// In that case, we'll do some extra checks for matching mounts.
54375526
let trackedPath: Array<PathFrame> | null = null;
54385527
let trackedPathMatchFiber: Fiber | null = null; // This is the deepest unfiltered match of a Fiber.
5439-
let trackedPathMatchInstance: DevToolsInstance | null = null; // This is the deepest matched filtered Instance.
5528+
let trackedPathMatchInstance: FiberInstance | VirtualInstance | null = null; // This is the deepest matched filtered Instance.
54405529
let trackedPathMatchDepth = -1;
54415530
let mightBeOnTrackedPath = false;
54425531

@@ -5455,7 +5544,7 @@ export function attach(
54555544
// The return value signals whether we should keep matching siblings or not.
54565545
function updateTrackedPathStateBeforeMount(
54575546
fiber: Fiber,
5458-
fiberInstance: null | FiberInstance,
5547+
fiberInstance: null | FiberInstance | FilteredFiberInstance,
54595548
): boolean {
54605549
if (trackedPath === null || !mightBeOnTrackedPath) {
54615550
// Fast path: there's nothing to track so do nothing and ignore siblings.
@@ -5484,7 +5573,7 @@ export function attach(
54845573
) {
54855574
// We have our next match.
54865575
trackedPathMatchFiber = fiber;
5487-
if (fiberInstance !== null) {
5576+
if (fiberInstance !== null && fiberInstance.kind === FIBER_INSTANCE) {
54885577
trackedPathMatchInstance = fiberInstance;
54895578
}
54905579
trackedPathMatchDepth++;

0 commit comments

Comments
 (0)