Skip to content

Commit ed94ea1

Browse files
sebmarkbagehoxyq
andauthored
[DevTools] Allow Highlighting/Inspect HostSingletons/Hoistables and Resources (#30584)
Basically the new Float types needs to be supported. Resources are a bit special because they're a DOM specific type but we can expect any other implementation using resources to provide and instance on this field if needed. There's a slightly related case for the reverse lookup. You can already select a singleton or hoistable (that's not a resource) in the browser elements panel and it'll select the corresponding node in the RDT Components panel. That works because it uses the same mechanism as event dispatching and those need to be able to receive events. However, you can't select a resource. Because that's conceptually one to many. We could in principle just search the tree for the first one or keep a map of currently mounted resources and just pick the first fiber that created it. So that you can select a resource and see what created it. Particularly useful when there's only one Fiber which is most of the time. --------- Co-authored-by: Ruslan Lesiutin <rdlesyutin@gmail.com>
1 parent 47337a8 commit ed94ea1

File tree

5 files changed

+52
-23
lines changed

5 files changed

+52
-23
lines changed

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

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2695,11 +2695,11 @@ export function attach(
26952695
// If we're tracing updates and we've bailed out before reaching a host node,
26962696
// we should fall back to recursively marking the nearest host descendants for highlight.
26972697
if (traceNearestHostComponentUpdate) {
2698-
const hostFibers = findAllCurrentHostFibers(
2698+
const hostInstances = findAllCurrentHostInstances(
26992699
getFiberInstanceThrows(nextFiber),
27002700
);
2701-
hostFibers.forEach(hostFiber => {
2702-
traceUpdatesForNodes.add(hostFiber.stateNode);
2701+
hostInstances.forEach(hostInstance => {
2702+
traceUpdatesForNodes.add(hostInstance);
27032703
});
27042704
}
27052705
}
@@ -2943,31 +2943,54 @@ export function attach(
29432943
currentRootID = -1;
29442944
}
29452945

2946-
function findAllCurrentHostFibers(
2946+
function getResourceInstance(fiber: Fiber): HostInstance | null {
2947+
if (fiber.tag === HostHoistable) {
2948+
const resource = fiber.memoizedState;
2949+
// Feature Detect a DOM Specific Instance of a Resource
2950+
if (
2951+
typeof resource === 'object' &&
2952+
resource !== null &&
2953+
resource.instance != null
2954+
) {
2955+
return resource.instance;
2956+
}
2957+
}
2958+
return null;
2959+
}
2960+
2961+
function findAllCurrentHostInstances(
29472962
fiberInstance: FiberInstance,
2948-
): $ReadOnlyArray<Fiber> {
2949-
const fibers = [];
2963+
): $ReadOnlyArray<HostInstance> {
2964+
const hostInstances = [];
29502965
const fiber = findCurrentFiberUsingSlowPathByFiberInstance(fiberInstance);
29512966
if (!fiber) {
2952-
return fibers;
2967+
return hostInstances;
29532968
}
29542969

29552970
// Next we'll drill down this component to find all HostComponent/Text.
29562971
let node: Fiber = fiber;
29572972
while (true) {
2958-
if (node.tag === HostComponent || node.tag === HostText) {
2959-
fibers.push(node);
2973+
if (
2974+
node.tag === HostComponent ||
2975+
node.tag === HostText ||
2976+
node.tag === HostSingleton ||
2977+
node.tag === HostHoistable
2978+
) {
2979+
const hostInstance = node.stateNode || getResourceInstance(node);
2980+
if (hostInstance) {
2981+
hostInstances.push(hostInstance);
2982+
}
29602983
} else if (node.child) {
29612984
node.child.return = node;
29622985
node = node.child;
29632986
continue;
29642987
}
29652988
if (node === fiber) {
2966-
return fibers;
2989+
return hostInstances;
29672990
}
29682991
while (!node.sibling) {
29692992
if (!node.return || node.return === fiber) {
2970-
return fibers;
2993+
return hostInstances;
29712994
}
29722995
node = node.return;
29732996
}
@@ -2976,7 +2999,7 @@ export function attach(
29762999
}
29773000
// Flow needs the return here, but ESLint complains about it.
29783001
// eslint-disable-next-line no-unreachable
2979-
return fibers;
3002+
return hostInstances;
29803003
}
29813004

29823005
function findHostInstancesForElementID(id: number) {
@@ -2996,8 +3019,8 @@ export function attach(
29963019
return null;
29973020
}
29983021

2999-
const hostFibers = findAllCurrentHostFibers(devtoolsInstance);
3000-
return hostFibers.map(hostFiber => hostFiber.stateNode).filter(Boolean);
3022+
const hostInstances = findAllCurrentHostInstances(devtoolsInstance);
3023+
return hostInstances;
30013024
} catch (err) {
30023025
// The fiber might have unmounted by now.
30033026
return null;

packages/react-devtools-shared/src/backend/types.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export type GetElementIDForHostInstance = (
9494
) => number | null;
9595
export type FindHostInstancesForElementID = (
9696
id: number,
97-
) => ?Array<HostInstance>;
97+
) => null | $ReadOnlyArray<HostInstance>;
9898

9999
export type ReactProviderType<T> = {
100100
$$typeof: symbol | number,

packages/react-devtools-shared/src/backend/views/Highlighter/Highlighter.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,15 @@ export function hideOverlay(agent: Agent): void {
3838
: hideOverlayWeb();
3939
}
4040

41-
function showOverlayNative(elements: Array<HostInstance>, agent: Agent): void {
41+
function showOverlayNative(
42+
elements: $ReadOnlyArray<HostInstance>,
43+
agent: Agent,
44+
): void {
4245
agent.emit('showNativeHighlight', elements);
4346
}
4447

4548
function showOverlayWeb(
46-
elements: Array<HTMLElement>,
49+
elements: $ReadOnlyArray<HTMLElement>,
4750
componentName: string | null,
4851
agent: Agent,
4952
hideAfterTimeout: boolean,
@@ -64,12 +67,17 @@ function showOverlayWeb(
6467
}
6568

6669
export function showOverlay(
67-
elements: Array<HostInstance>,
70+
elements: $ReadOnlyArray<HostInstance>,
6871
componentName: string | null,
6972
agent: Agent,
7073
hideAfterTimeout: boolean,
7174
): void {
7275
return isReactNativeEnvironment()
7376
? showOverlayNative(elements, agent)
74-
: showOverlayWeb((elements: any), componentName, agent, hideAfterTimeout);
77+
: showOverlayWeb(
78+
(elements: $ReadOnlyArray<any>),
79+
componentName,
80+
agent,
81+
hideAfterTimeout,
82+
);
7583
}

packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export default class Overlay {
187187
}
188188
}
189189

190-
inspect(nodes: Array<HTMLElement>, name?: ?string) {
190+
inspect(nodes: $ReadOnlyArray<HTMLElement>, name?: ?string) {
191191
// We can't get the size of text nodes or comment nodes. React as of v15
192192
// heavily uses comment nodes to delimit text.
193193
const elements = nodes.filter(node => node.nodeType === Node.ELEMENT_NODE);

packages/react-devtools-shared/src/backend/views/Highlighter/index.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import Agent from 'react-devtools-shared/src/backend/agent';
1313
import {hideOverlay, showOverlay} from './Highlighter';
1414

1515
import type {BackendBridge} from 'react-devtools-shared/src/bridge';
16-
import type {HostInstance} from '../../types';
1716

1817
// This plug-in provides in-page highlighting of the selected element.
1918
// It is used by the browser extension and the standalone DevTools shell (when connected to a browser).
@@ -113,8 +112,7 @@ export default function setupHighlighter(
113112
return;
114113
}
115114

116-
const nodes: ?Array<HostInstance> =
117-
renderer.findHostInstancesForElementID(id);
115+
const nodes = renderer.findHostInstancesForElementID(id);
118116

119117
if (nodes != null && nodes[0] != null) {
120118
const node = nodes[0];

0 commit comments

Comments
 (0)