Skip to content

Commit 8619997

Browse files
committed
Track all public HostInstances in a Map
This lets us get from a HostInstance to the nearest DevToolsInstance without relying on findNearestUnfilteredElementID and fiberToDevToolsInstanceMap.
1 parent f90a6bc commit 8619997

File tree

1 file changed

+109
-80
lines changed
  • packages/react-devtools-shared/src/backend/fiber

1 file changed

+109
-80
lines changed

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

Lines changed: 109 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -732,35 +732,85 @@ const fiberToFiberInstanceMap: Map<Fiber, FiberInstance> = new Map();
732732
// operations that should be the same whether the current and work-in-progress Fiber is used.
733733
const idToDevToolsInstanceMap: Map<number, DevToolsInstance> = new Map();
734734

735-
// Map of resource DOM nodes to all the Fibers that depend on it.
736-
const hostResourceToFiberMap: Map<HostInstance, Set<Fiber>> = new Map();
735+
// Map of canonical HostInstances to the nearest parent DevToolsInstance.
736+
const publicInstanceToDevToolsInstanceMap: Map<HostInstance, DevToolsInstance> =
737+
new Map();
738+
// Map of resource DOM nodes to all the nearest DevToolsInstances that depend on it.
739+
const hostResourceToDevToolsInstanceMap: Map<
740+
HostInstance,
741+
Set<DevToolsInstance>,
742+
> = new Map();
743+
744+
function getPublicInstance(instance: HostInstance): HostInstance {
745+
// Typically the PublicInstance and HostInstance is the same thing but not in Fabric.
746+
// So we need to detect this and use that as the public instance.
747+
return typeof instance === 'object' &&
748+
instance !== null &&
749+
typeof instance.canonical === 'object'
750+
? (instance.canonical: any)
751+
: instance;
752+
}
753+
754+
function aquireHostInstance(
755+
nearestInstance: DevToolsInstance,
756+
hostInstance: HostInstance,
757+
): void {
758+
const publicInstance = getPublicInstance(hostInstance);
759+
publicInstanceToDevToolsInstanceMap.set(publicInstance, nearestInstance);
760+
}
761+
762+
function releaseHostInstance(
763+
nearestInstance: DevToolsInstance,
764+
hostInstance: HostInstance,
765+
): void {
766+
const publicInstance = getPublicInstance(hostInstance);
767+
if (
768+
publicInstanceToDevToolsInstanceMap.get(publicInstance) === nearestInstance
769+
) {
770+
publicInstanceToDevToolsInstanceMap.delete(publicInstance);
771+
}
772+
}
737773

738774
function aquireHostResource(
739-
fiber: Fiber,
775+
nearestInstance: DevToolsInstance,
740776
resource: ?{instance?: HostInstance},
741777
): void {
742778
const hostInstance = resource && resource.instance;
743779
if (hostInstance) {
744-
let resourceFibers = hostResourceToFiberMap.get(hostInstance);
745-
if (resourceFibers === undefined) {
746-
resourceFibers = new Set();
747-
hostResourceToFiberMap.set(hostInstance, resourceFibers);
780+
const publicInstance = getPublicInstance(hostInstance);
781+
let resourceInstances =
782+
hostResourceToDevToolsInstanceMap.get(publicInstance);
783+
if (resourceInstances === undefined) {
784+
resourceInstances = new Set();
785+
hostResourceToDevToolsInstanceMap.set(publicInstance, resourceInstances);
786+
// Store the first match in the main map for quick access when selecting DOM node.
787+
publicInstanceToDevToolsInstanceMap.set(publicInstance, nearestInstance);
748788
}
749-
resourceFibers.add(fiber);
789+
resourceInstances.add(nearestInstance);
750790
}
751791
}
752792

753793
function releaseHostResource(
754-
fiber: Fiber,
794+
nearestInstance: DevToolsInstance,
755795
resource: ?{instance?: HostInstance},
756796
): void {
757797
const hostInstance = resource && resource.instance;
758798
if (hostInstance) {
759-
const resourceFibers = hostResourceToFiberMap.get(hostInstance);
760-
if (resourceFibers !== undefined) {
761-
resourceFibers.delete(fiber);
762-
if (resourceFibers.size === 0) {
763-
hostResourceToFiberMap.delete(hostInstance);
799+
const publicInstance = getPublicInstance(hostInstance);
800+
const resourceInstances =
801+
hostResourceToDevToolsInstanceMap.get(publicInstance);
802+
if (resourceInstances !== undefined) {
803+
resourceInstances.delete(nearestInstance);
804+
if (resourceInstances.size === 0) {
805+
hostResourceToDevToolsInstanceMap.delete(publicInstance);
806+
publicInstanceToDevToolsInstanceMap.delete(publicInstance);
807+
} else if (publicInstanceToDevToolsInstanceMap.get(publicInstance) === nearestInstance) {
808+
// This was the first one. Store the next first one in the main map for easy access.
809+
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
810+
for (const firstInstance of resourceInstances) {
811+
publicInstanceToDevToolsInstanceMap.set(firstInstance, nearestInstance);
812+
break;
813+
}
764814
}
765815
}
766816
}
@@ -1448,6 +1498,18 @@ export function attach(
14481498
fiberToFiberInstanceMap.delete(alternate);
14491499
}
14501500
}
1501+
1502+
// TODO: This is not enough if this Fiber was filtered since we don't end up
1503+
// untracking it. We could use a WeakMap but that doesn't work for Paper tags.
1504+
if (fiber.tag === HostHoistable) {
1505+
releaseHostResource(fiberInstance, fiber.memoizedState);
1506+
} else if (
1507+
fiber.tag === HostComponent ||
1508+
fiber.tag === HostText ||
1509+
fiber.tag === HostSingleton
1510+
) {
1511+
releaseHostInstance(fiberInstance, fiber.stateNode);
1512+
}
14511513
}
14521514

14531515
function getChangeDescription(
@@ -2557,7 +2619,21 @@ export function attach(
25572619
}
25582620

25592621
if (fiber.tag === HostHoistable) {
2560-
aquireHostResource(fiber, fiber.memoizedState);
2622+
const nearestInstance = reconcilingParent;
2623+
if (nearestInstance === null) {
2624+
throw new Error('Did not expect a host hoistable to be the root');
2625+
}
2626+
aquireHostResource(nearestInstance, fiber.memoizedState);
2627+
} else if (
2628+
fiber.tag === HostComponent ||
2629+
fiber.tag === HostText ||
2630+
fiber.tag === HostSingleton
2631+
) {
2632+
const nearestInstance = reconcilingParent;
2633+
if (nearestInstance === null) {
2634+
throw new Error('Did not expect a host hoistable to be the root');
2635+
}
2636+
aquireHostInstance(nearestInstance, fiber.stateNode);
25612637
}
25622638

25632639
if (fiber.tag === SuspenseComponent) {
@@ -3159,8 +3235,12 @@ export function attach(
31593235
}
31603236
try {
31613237
if (nextFiber.tag === HostHoistable) {
3162-
releaseHostResource(prevFiber, prevFiber.memoizedState);
3163-
aquireHostResource(nextFiber, nextFiber.memoizedState);
3238+
const nearestInstance = reconcilingParent;
3239+
if (nearestInstance === null) {
3240+
throw new Error('Did not expect a host hoistable to be the root');
3241+
}
3242+
releaseHostResource(nearestInstance, prevFiber.memoizedState);
3243+
aquireHostResource(nearestInstance, nextFiber.memoizedState);
31643244
}
31653245

31663246
const isSuspense = nextFiber.tag === SuspenseComponent;
@@ -3639,84 +3719,33 @@ export function attach(
36393719
}
36403720

36413721
function getNearestMountedHostInstance(
3642-
hostInstance: HostInstance,
3722+
publicInstance: HostInstance,
36433723
): null | HostInstance {
3644-
const mountedFiber = renderer.findFiberByHostInstance(hostInstance);
3724+
// TODO: Remove dependency on findFiberByHostInstance.
3725+
const mountedFiber = renderer.findFiberByHostInstance(publicInstance);
36453726
if (mountedFiber != null) {
3646-
if (mountedFiber.stateNode !== hostInstance) {
3727+
if (mountedFiber.stateNode !== publicInstance) {
36473728
// If it's not a perfect match the specific one might be a resource.
36483729
// We don't need to look at any parents because host resources don't have
36493730
// children so it won't be in any parent if it's not this one.
3650-
if (hostResourceToFiberMap.has(hostInstance)) {
3651-
return hostInstance;
3731+
if (publicInstanceToDevToolsInstanceMap.has(publicInstance)) {
3732+
return publicInstance;
36523733
}
36533734
}
36543735
return mountedFiber.stateNode;
36553736
}
3656-
if (hostResourceToFiberMap.has(hostInstance)) {
3657-
return hostInstance;
3658-
}
3659-
return null;
3660-
}
3661-
3662-
function findNearestUnfilteredElementID(searchFiber: Fiber) {
3663-
let fiber: null | Fiber = searchFiber;
3664-
while (fiber !== null) {
3665-
const fiberInstance = getFiberInstanceUnsafe(fiber);
3666-
if (fiberInstance !== null) {
3667-
// TODO: Ideally we would not have any filtered FiberInstances which
3668-
// would make this logic much simpler. Unfortunately, we sometimes
3669-
// eagerly add to the map and some times don't eagerly clean it up.
3670-
// TODO: If the fiber is filtered, the FiberInstance wouldn't really
3671-
// exist which would mean that we also don't have a way to get to the
3672-
// VirtualInstances.
3673-
if (!shouldFilterFiber(fiberInstance.data)) {
3674-
return fiberInstance.id;
3675-
}
3676-
// We couldn't use this Fiber but we might have a VirtualInstance
3677-
// that is the nearest unfiltered instance.
3678-
let parentInstance = fiberInstance.parent;
3679-
while (parentInstance !== null) {
3680-
if (parentInstance.kind === FIBER_INSTANCE) {
3681-
// If we find a parent Fiber, it might not be the nearest parent
3682-
// so we break out and continue walking the Fiber tree instead.
3683-
break;
3684-
} else {
3685-
if (!shouldFilterVirtual(parentInstance.data)) {
3686-
return parentInstance.id;
3687-
}
3688-
}
3689-
parentInstance = parentInstance.parent;
3690-
}
3691-
}
3692-
fiber = fiber.return;
3737+
if (publicInstanceToDevToolsInstanceMap.has(publicInstance)) {
3738+
return publicInstance;
36933739
}
36943740
return null;
36953741
}
36963742

36973743
function getElementIDForHostInstance(
3698-
hostInstance: HostInstance,
3699-
findNearestUnfilteredAncestor: boolean = false,
3744+
publicInstance: HostInstance,
37003745
): number | null {
3701-
const resourceFibers = hostResourceToFiberMap.get(hostInstance);
3702-
if (resourceFibers !== undefined) {
3703-
// This is a resource. Find the first unfiltered instance.
3704-
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
3705-
for (const resourceFiber of resourceFibers) {
3706-
const elementID = findNearestUnfilteredElementID(resourceFiber);
3707-
if (elementID !== null) {
3708-
return elementID;
3709-
}
3710-
}
3711-
// If we don't find one, fallthrough to select the parent instead.
3712-
}
3713-
const fiber = renderer.findFiberByHostInstance(hostInstance);
3714-
if (fiber != null) {
3715-
if (!findNearestUnfilteredAncestor) {
3716-
// TODO: Remove this option. It's not used.
3717-
return getFiberIDThrows(fiber);
3718-
}
3719-
return findNearestUnfilteredElementID(fiber);
3746+
const instance = publicInstanceToDevToolsInstanceMap.get(publicInstance);
3747+
if (instance !== undefined) {
3748+
return instance.id;
37203749
}
37213750
return null;
37223751
}

0 commit comments

Comments
 (0)