696
// operations that should be the same whether the current and work-in-progress Fiber is used.
697
const idToDevToolsInstanceMap: Map<number, DevToolsInstance> = new Map();
698
699
+// Map of resource DOM nodes to all the Fibers that depend on it.
700
+const hostResourceToFiberMap: Map<HostInstance, Set<Fiber>> = new Map();
701
+
702
+function aquireHostResource(
703
+ fiber: Fiber,
704
+ resource: ?{instance?: HostInstance},
705
+): void {
706
+ const hostInstance = resource && resource.instance;
707
+ if (hostInstance) {
708
+ let resourceFibers = hostResourceToFiberMap.get(hostInstance);
709
+ if (resourceFibers === undefined) {
710
+ resourceFibers = new Set();
711
+ hostResourceToFiberMap.set(hostInstance, resourceFibers);
712
+ }
713
+ resourceFibers.add(fiber);
714
+ }
715
+}
716
+
717
+function releaseHostResource(
718
+ fiber: Fiber,
719
+ resource: ?{instance?: HostInstance},
720
+): void {
721
+ const hostInstance = resource && resource.instance;
722
+ if (hostInstance) {
723
+ const resourceFibers = hostResourceToFiberMap.get(hostInstance);
724
+ if (resourceFibers !== undefined) {
725
+ resourceFibers.delete(fiber);
726
+ if (resourceFibers.size === 0) {
727
+ hostResourceToFiberMap.delete(hostInstance);
728
+ }
729
+ }
730
+ }
731
+}
732
+
733
export function attach(
734
hook: DevToolsHook,
735
rendererID: number,
2317
// because we don't want to highlight every host node inside of a newly mounted subtree.
2318
}
2319
2320
+ if (fiber.tag === HostHoistable) {
2321
+ aquireHostResource(fiber, fiber.memoizedState);
2322
+ }
2323
+
2324
if (fiber.tag === SuspenseComponent) {
2325
const isTimedOut = fiber.memoizedState !== null;
2326
if (isTimedOut) {
2382
2383
// We might meet a nested Suspense on our way.
2384
const isTimedOutSuspense =
2347
- fiber.tag === ReactTypeOfWork.SuspenseComponent &&
2348
- fiber.memoizedState !== null;
2385
+ fiber.tag === SuspenseComponent && fiber.memoizedState !== null;
2386
+
2387
+ if (fiber.tag === HostHoistable) {
2388
+ releaseHostResource(fiber, fiber.memoizedState);
2389
+ }
2390
2391
let child = fiber.child;
2392
if (isTimedOutSuspense) {
2662
const newParentInstance = shouldIncludeInTree
2663
? fiberInstance
2664
: parentInstance;
2665
+
2666
+ if (nextFiber.tag === HostHoistable) {
2667
+ releaseHostResource(prevFiber, prevFiber.memoizedState);
2668
+ aquireHostResource(nextFiber, nextFiber.memoizedState);
2669
+ }
2670
+
2671
const isSuspense = nextFiber.tag === SuspenseComponent;
2672
let shouldResetChildren = false;
2673
// The behavior of timed-out Suspense trees is unique.
3117
function getNearestMountedHostInstance(
3118
hostInstance: HostInstance,
3119
): null | HostInstance {
3073
- const mountedHostInstance = renderer.findFiberByHostInstance(hostInstance);
3074
- if (mountedHostInstance != null) {
3075
- return mountedHostInstance.stateNode;
3120
+ const mountedFiber = renderer.findFiberByHostInstance(hostInstance);
3121
+ if (mountedFiber != null) {
3122
+ if (mountedFiber.stateNode !== hostInstance) {
3123
+ // If it's not a perfect match the specific one might be a resource.
3124
+ // We don't need to look at any parents because host resources don't have
3125
+ // children so it won't be in any parent if it's not this one.
3126
+ if (hostResourceToFiberMap.has(hostInstance)) {
3127
+ return hostInstance;
3128
+ }
3129
+ }
3130
+ return mountedFiber.stateNode;
3131
+ }
3132
+ if (hostResourceToFiberMap.has(hostInstance)) {
3133
+ return hostInstance;
3134
+ }
3135
+ return null;
3136
+ }
3137
+
3138
+ function findNearestUnfilteredElementID(searchFiber: Fiber) {
3139
+ let fiber: null | Fiber = searchFiber;
3140
+ while (fiber !== null) {
3141
+ const fiberInstance = getFiberInstanceUnsafe(fiber);
3142
+ if (fiberInstance !== null) {
3143
+ // TODO: Ideally we would not have any filtered FiberInstances which
3144
+ // would make this logic much simpler. Unfortunately, we sometimes
3145
+ // eagerly add to the map and some times don't eagerly clean it up.
3146
+ // TODO: If the fiber is filtered, the FiberInstance wouldn't really
3147
+ // exist which would mean that we also don't have a way to get to the
3148
+ // VirtualInstances.
3149
+ if (!shouldFilterFiber(fiberInstance.data)) {
3150
+ return fiberInstance.id;
3151
+ }
3152
+ // We couldn't use this Fiber but we might have a VirtualInstance
3153
+ // that is the nearest unfiltered instance.
3154
+ let parentInstance = fiberInstance.parent;
3155
+ while (parentInstance !== null) {
3156
+ if (parentInstance.kind === FIBER_INSTANCE) {
3157
+ // If we find a parent Fiber, it might not be the nearest parent
3158
+ // so we break out and continue walking the Fiber tree instead.
3159
+ break;
3160
+ } else {
3161
+ if (!shouldFilterVirtual(parentInstance.data)) {
3162
+ return parentInstance.id;
3163
+ }
3164
+ }
3165
+ parentInstance = parentInstance.parent;
3166
+ }
3167
+ }
3168
+ fiber = fiber.return;
3169
}
3170
return null;
3171
}
3174
hostInstance: HostInstance,
3175
findNearestUnfilteredAncestor: boolean = false,
3176
): number | null {
3084
- let fiber = renderer.findFiberByHostInstance(hostInstance);
3177
+ const resourceFibers = hostResourceToFiberMap.get(hostInstance);
3178
+ if (resourceFibers !== undefined) {
3179
+ // This is a resource. Find the first unfiltered instance.
3180
+ // eslint-disable-next-line no-for-of-loops/no-for-of-loops
3181
+ for (const resourceFiber of resourceFibers) {
3182
+ const elementID = findNearestUnfilteredElementID(resourceFiber);
3183
+ if (elementID !== null) {
3184
+ return elementID;
3185
+ }
3186
+ }
3187
+ // If we don't find one, fallthrough to select the parent instead.
3188
+ }
3189
+ const fiber = renderer.findFiberByHostInstance(hostInstance);
3190
if (fiber != null) {
3191
if (!findNearestUnfilteredAncestor) {
3192
// TODO: Remove this option. It's not used.
3193
return getFiberIDThrows(fiber);
3194
}
3090
- while (fiber !== null) {
3091
- const fiberInstance = getFiberInstanceUnsafe(fiber);
3092
- if (fiberInstance !== null) {
3093
- // TODO: Ideally we would not have any filtered FiberInstances which
3094
- // would make this logic much simpler. Unfortunately, we sometimes
3095
- // eagerly add to the map and some times don't eagerly clean it up.
3096
- // TODO: If the fiber is filtered, the FiberInstance wouldn't really
3097
- // exist which would mean that we also don't have a way to get to the
3098
- // VirtualInstances.
3099
- if (!shouldFilterFiber(fiberInstance.data)) {
3100
- return fiberInstance.id;
3101
- }
3102
- // We couldn't use this Fiber but we might have a VirtualInstance
3103
- // that is the nearest unfiltered instance.
3104
- let parentInstance = fiberInstance.parent;
3105
- while (parentInstance !== null) {
3106
- if (parentInstance.kind === FIBER_INSTANCE) {
3107
- // If we find a parent Fiber, it might not be the nearest parent
3108
- // so we break out and continue walking the Fiber tree instead.
3109
- break;
3110
- } else {
3111
- if (!shouldFilterVirtual(parentInstance.data)) {
3112
- return parentInstance.id;
3113
- }
3114
- }
3115
- parentInstance = parentInstance.parent;
3116
- }
3117
- }
3118
- fiber = fiber.return;
3119
- }
3195
+ return findNearestUnfilteredElementID(fiber);
3196
}
3197
return null;
3198
}