Skip to content

Commit 3e92eb0

Browse files
authored
[DevTools] find best renderer when inspecting (#24665)
* [DevTools] find best renderer when inspecting * fix lint * fix test * fix lint * move logic to agent * fix lint * style improvements per review comments * fix lint & flow * re-add try catch for safety
1 parent 42b330c commit 3e92eb0

File tree

7 files changed

+59
-27
lines changed

7 files changed

+59
-27
lines changed

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,17 +309,31 @@ export default class Agent extends EventEmitter<{|
309309
return renderer.getInstanceAndStyle(id);
310310
}
311311

312-
getIDForNode(node: Object): number | null {
312+
getBestMatchingRendererInterface(node: Object): RendererInterface | null {
313+
let bestMatch = null;
313314
for (const rendererID in this._rendererInterfaces) {
314315
const renderer = ((this._rendererInterfaces[
315316
(rendererID: any)
316317
]: any): RendererInterface);
318+
const fiber = renderer.getFiberForNative(node);
319+
if (fiber !== null) {
320+
// check if fiber.stateNode is matching the original hostInstance
321+
if (fiber.stateNode === node) {
322+
return renderer;
323+
} else if (bestMatch === null) {
324+
bestMatch = renderer;
325+
}
326+
}
327+
}
328+
// if an exact match is not found, return the first valid renderer as fallback
329+
return bestMatch;
330+
}
317331

332+
getIDForNode(node: Object): number | null {
333+
const rendererInterface = this.getBestMatchingRendererInterface(node);
334+
if (rendererInterface != null) {
318335
try {
319-
const id = renderer.getFiberIDForNative(node, true);
320-
if (id !== null) {
321-
return id;
322-
}
336+
return rendererInterface.getFiberIDForNative(node, true);
323337
} catch (error) {
324338
// Some old React versions might throw if they can't find a match.
325339
// If so we should ignore it...

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ export function attach(
148148

149149
let getInternalIDForNative: GetFiberIDForNative = ((null: any): GetFiberIDForNative);
150150
let findNativeNodeForInternalID: (id: number) => ?NativeType;
151+
let getFiberForNative = (node: NativeType) => {
152+
// Not implemented.
153+
return null;
154+
};
151155

152156
if (renderer.ComponentTree) {
153157
getInternalIDForNative = (node, findNearestUnfilteredAncestor) => {
@@ -160,6 +164,9 @@ export function attach(
160164
const internalInstance = idToInternalInstanceMap.get(id);
161165
return renderer.ComponentTree.getNodeFromInstance(internalInstance);
162166
};
167+
getFiberForNative = (node: NativeType) => {
168+
return renderer.ComponentTree.getClosestInstanceFromNode(node);
169+
};
163170
} else if (renderer.Mount.getID && renderer.Mount.getNode) {
164171
getInternalIDForNative = (node, findNearestUnfilteredAncestor) => {
165172
// Not implemented.
@@ -1094,6 +1101,7 @@ export function attach(
10941101
flushInitialOperations,
10951102
getBestMatchForTrackedPath,
10961103
getDisplayNameForFiberID,
1104+
getFiberForNative,
10971105
getFiberIDForNative: getInternalIDForNative,
10981106
getInstanceAndStyle,
10991107
findNativeNodesForFiberID: (id: number) => {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2818,6 +2818,10 @@ export function attach(
28182818
return fiber != null ? getDisplayNameForFiber(((fiber: any): Fiber)) : null;
28192819
}
28202820

2821+
function getFiberForNative(hostInstance) {
2822+
return renderer.findFiberByHostInstance(hostInstance);
2823+
}
2824+
28212825
function getFiberIDForNative(
28222826
hostInstance,
28232827
findNearestUnfilteredAncestor = false,
@@ -4490,6 +4494,7 @@ export function attach(
44904494
flushInitialOperations,
44914495
getBestMatchForTrackedPath,
44924496
getDisplayNameForFiberID,
4497+
getFiberForNative,
44934498
getFiberIDForNative,
44944499
getInstanceAndStyle,
44954500
getOwnersList,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export type Lane = number;
9393
export type Lanes = number;
9494

9595
export type ReactRenderer = {
96-
findFiberByHostInstance: (hostInstance: NativeType) => ?Fiber,
96+
findFiberByHostInstance: (hostInstance: NativeType) => Fiber | null,
9797
version: string,
9898
rendererPackageName: string,
9999
bundleType: BundleType,
@@ -350,6 +350,7 @@ export type RendererInterface = {
350350
findNativeNodesForFiberID: FindNativeNodesForFiberID,
351351
flushInitialOperations: () => void,
352352
getBestMatchForTrackedPath: () => PathMatch | null,
353+
getFiberForNative: (component: NativeType) => Fiber | null,
353354
getFiberIDForNative: GetFiberIDForNative,
354355
getDisplayNameForFiberID: GetDisplayNameForFiberID,
355356
getInstanceAndStyle(id: number): InstanceAndStyle,

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
* @flow
88
*/
99

10+
import type Agent from 'react-devtools-shared/src/backend/agent';
11+
1012
import Overlay from './Overlay';
1113

1214
const SHOW_DURATION = 2000;
@@ -26,6 +28,7 @@ export function hideOverlay() {
2628
export function showOverlay(
2729
elements: Array<HTMLElement> | null,
2830
componentName: string | null,
31+
agent: Agent,
2932
hideAfterTimeout: boolean,
3033
) {
3134
// TODO (npm-packages) Detect RN and support it somehow
@@ -42,7 +45,7 @@ export function showOverlay(
4245
}
4346

4447
if (overlay === null) {
45-
overlay = new Overlay();
48+
overlay = new Overlay(agent);
4649
}
4750

4851
overlay.inspect(elements, componentName);

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

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99

1010
import {getElementDimensions, getNestedBoundingClientRect} from '../utils';
1111

12-
const assign = Object.assign;
13-
14-
import type {DevToolsHook} from 'react-devtools-shared/src/backend/types';
1512
import type {Rect} from '../utils';
13+
import type Agent from 'react-devtools-shared/src/backend/agent';
1614

1715
type Box = {|top: number, left: number, width: number, height: number|};
1816

17+
const assign = Object.assign;
18+
1919
// Note that the Overlay components are not affected by the active Theme,
2020
// because they highlight elements in the main Chrome window (outside of devtools).
2121
// The colors below were chosen to roughly match those used by Chrome devtools.
@@ -153,8 +153,9 @@ export default class Overlay {
153153
container: HTMLElement;
154154
tip: OverlayTip;
155155
rects: Array<OverlayRect>;
156+
agent: Agent;
156157

157-
constructor() {
158+
constructor(agent: Agent) {
158159
// Find the root window, because overlays are positioned relative to it.
159160
const currentWindow = window.__REACT_DEVTOOLS_TARGET_WINDOW__ || window;
160161
this.window = currentWindow;
@@ -170,6 +171,8 @@ export default class Overlay {
170171
this.tip = new OverlayTip(doc, this.container);
171172
this.rects = [];
172173

174+
this.agent = agent;
175+
173176
doc.body.appendChild(this.container);
174177
}
175178

@@ -230,22 +233,20 @@ export default class Overlay {
230233
name = elements[0].nodeName.toLowerCase();
231234

232235
const node = elements[0];
233-
const hook: DevToolsHook =
234-
node.ownerDocument.defaultView.__REACT_DEVTOOLS_GLOBAL_HOOK__;
235-
if (hook != null && hook.rendererInterfaces != null) {
236-
let ownerName = null;
237-
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
238-
for (const rendererInterface of hook.rendererInterfaces.values()) {
239-
const id = rendererInterface.getFiberIDForNative(node, true);
240-
if (id !== null) {
241-
ownerName = rendererInterface.getDisplayNameForFiberID(id, true);
242-
break;
236+
const rendererInterface = this.agent.getBestMatchingRendererInterface(
237+
node,
238+
);
239+
if (rendererInterface) {
240+
const id = rendererInterface.getFiberIDForNative(node, true);
241+
if (id) {
242+
const ownerName = rendererInterface.getDisplayNameForFiberID(
243+
id,
244+
true,
245+
);
246+
if (ownerName) {
247+
name += ' (in ' + ownerName + ')';
243248
}
244249
}
245-
246-
if (ownerName) {
247-
name += ' (in ' + ownerName + ')';
248-
}
249250
}
250251
}
251252

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export default function setupHighlighter(
118118
node.scrollIntoView({block: 'nearest', inline: 'nearest'});
119119
}
120120

121-
showOverlay(nodes, displayName, hideAfterTimeout);
121+
showOverlay(nodes, displayName, agent, hideAfterTimeout);
122122

123123
if (openNativeElementsPanel) {
124124
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0 = node;
@@ -171,7 +171,7 @@ export default function setupHighlighter(
171171

172172
// Don't pass the name explicitly.
173173
// It will be inferred from DOM tag and Fiber owner.
174-
showOverlay([target], null, false);
174+
showOverlay([target], null, agent, false);
175175

176176
selectFiberForNode(target);
177177
}

0 commit comments

Comments
 (0)