[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
Mengdi Chen committed
Jun 8, 2022 at 16:01 UTC
3e92eb0fce762aca49d2ea18b869a2fd8a75089e
7 files changed
+59
-27
packages/react-devtools-shared/src/backend/agent.js
+19
-5
@@ -309,17 +309,31 @@ export default class Agent extends EventEmitter<{|
309
return renderer.getInstanceAndStyle(id);
310
}
311
312
- getIDForNode(node: Object): number | null {
312
+ getBestMatchingRendererInterface(node: Object): RendererInterface | null {
313
+ let bestMatch = null;
314
for (const rendererID in this._rendererInterfaces) {
315
const renderer = ((this._rendererInterfaces[
316
(rendererID: any)
317
]: 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
+ }
331
332
+ getIDForNode(node: Object): number | null {
333
+ const rendererInterface = this.getBestMatchingRendererInterface(node);
334
+ if (rendererInterface != null) {
335
try {
319
- const id = renderer.getFiberIDForNative(node, true);
320
- if (id !== null) {
321
- return id;
322
- }
336
+ return rendererInterface.getFiberIDForNative(node, true);
337
} catch (error) {
338
// Some old React versions might throw if they can't find a match.
339
// If so we should ignore it...
packages/react-devtools-shared/src/backend/legacy/renderer.js
+8
@@ -148,6 +148,10 @@ export function attach(
148
149
let getInternalIDForNative: GetFiberIDForNative = ((null: any): GetFiberIDForNative);
150
let findNativeNodeForInternalID: (id: number) => ?NativeType;
151
+ let getFiberForNative = (node: NativeType) => {
152
+ // Not implemented.
153
+ return null;
154
+ };
155
156
if (renderer.ComponentTree) {
157
getInternalIDForNative = (node, findNearestUnfilteredAncestor) => {
@@ -160,6 +164,9 @@ export function attach(
164
const internalInstance = idToInternalInstanceMap.get(id);
165
return renderer.ComponentTree.getNodeFromInstance(internalInstance);
166
};
167
+ getFiberForNative = (node: NativeType) => {
168
+ return renderer.ComponentTree.getClosestInstanceFromNode(node);
169
+ };
170
} else if (renderer.Mount.getID && renderer.Mount.getNode) {
171
getInternalIDForNative = (node, findNearestUnfilteredAncestor) => {
172
// Not implemented.
@@ -1094,6 +1101,7 @@ export function attach(
1101
flushInitialOperations,
1102
getBestMatchForTrackedPath,
1103
getDisplayNameForFiberID,
1104
+ getFiberForNative,
1105
getFiberIDForNative: getInternalIDForNative,
1106
getInstanceAndStyle,
1107
findNativeNodesForFiberID: (id: number) => {
packages/react-devtools-shared/src/backend/renderer.js
+5
@@ -2818,6 +2818,10 @@ export function attach(
2818
return fiber != null ? getDisplayNameForFiber(((fiber: any): Fiber)) : null;
2819
}
2820
2821
+ function getFiberForNative(hostInstance) {
2822
+ return renderer.findFiberByHostInstance(hostInstance);
2823
+ }
2824
+
2825
function getFiberIDForNative(
2826
hostInstance,
2827
findNearestUnfilteredAncestor = false,
@@ -4490,6 +4494,7 @@ export function attach(
4494
flushInitialOperations,
4495
getBestMatchForTrackedPath,
4496
getDisplayNameForFiberID,
4497
+ getFiberForNative,
4498
getFiberIDForNative,
4499
getInstanceAndStyle,
4500
getOwnersList,
packages/react-devtools-shared/src/backend/types.js
+2
-1
@@ -93,7 +93,7 @@ export type Lane = number;
93
export type Lanes = number;
94
95
export type ReactRenderer = {
96
- findFiberByHostInstance: (hostInstance: NativeType) => ?Fiber,
96
+ findFiberByHostInstance: (hostInstance: NativeType) => Fiber | null,
97
version: string,
98
rendererPackageName: string,
99
bundleType: BundleType,
@@ -350,6 +350,7 @@ export type RendererInterface = {
350
findNativeNodesForFiberID: FindNativeNodesForFiberID,
351
flushInitialOperations: () => void,
352
getBestMatchForTrackedPath: () => PathMatch | null,
353
+ getFiberForNative: (component: NativeType) => Fiber | null,
354
getFiberIDForNative: GetFiberIDForNative,
355
getDisplayNameForFiberID: GetDisplayNameForFiberID,
356
getInstanceAndStyle(id: number): InstanceAndStyle,
packages/react-devtools-shared/src/backend/views/Highlighter/Highlighter.js
+4
-1
@@ -7,6 +7,8 @@
7
* @flow
8
*/
9
10
+import type Agent from 'react-devtools-shared/src/backend/agent';
11
+
12
import Overlay from './Overlay';
13
14
const SHOW_DURATION = 2000;
@@ -26,6 +28,7 @@ export function hideOverlay() {
28
export function showOverlay(
29
elements: Array<HTMLElement> | null,
30
componentName: string | null,
31
+ agent: Agent,
32
hideAfterTimeout: boolean,
33
) {
34
// TODO (npm-packages) Detect RN and support it somehow
@@ -42,7 +45,7 @@ export function showOverlay(
45
}
46
47
if (overlay === null) {
45
- overlay = new Overlay();
48
+ overlay = new Overlay(agent);
49
}
50
51
overlay.inspect(elements, componentName);
packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js
+19
-18
@@ -9,13 +9,13 @@
9
10
import {getElementDimensions, getNestedBoundingClientRect} from '../utils';
11
12
-const assign = Object.assign;
13
-
14
-import type {DevToolsHook} from 'react-devtools-shared/src/backend/types';
12
import type {Rect} from '../utils';
13
+import type Agent from 'react-devtools-shared/src/backend/agent';
14
15
type Box = {|top: number, left: number, width: number, height: number|};
16
17
+const assign = Object.assign;
18
+
19
// Note that the Overlay components are not affected by the active Theme,
20
// because they highlight elements in the main Chrome window (outside of devtools).
21
// The colors below were chosen to roughly match those used by Chrome devtools.
@@ -153,8 +153,9 @@ export default class Overlay {
153
container: HTMLElement;
154
tip: OverlayTip;
155
rects: Array<OverlayRect>;
156
+ agent: Agent;
157
157
- constructor() {
158
+ constructor(agent: Agent) {
159
// Find the root window, because overlays are positioned relative to it.
160
const currentWindow = window.__REACT_DEVTOOLS_TARGET_WINDOW__ || window;
161
this.window = currentWindow;
@@ -170,6 +171,8 @@ export default class Overlay {
171
this.tip = new OverlayTip(doc, this.container);
172
this.rects = [];
173
174
+ this.agent = agent;
175
+
176
doc.body.appendChild(this.container);
177
}
178
@@ -230,22 +233,20 @@ export default class Overlay {
233
name = elements[0].nodeName.toLowerCase();
234
235
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 + ')';
248
}
249
}
245
-
246
- if (ownerName) {
247
- name += ' (in ' + ownerName + ')';
248
- }
250
}
251
}
252
packages/react-devtools-shared/src/backend/views/Highlighter/index.js
+2
-2
@@ -118,7 +118,7 @@ export default function setupHighlighter(
118
node.scrollIntoView({block: 'nearest', inline: 'nearest'});
119
}
120
121
- showOverlay(nodes, displayName, hideAfterTimeout);
121
+ showOverlay(nodes, displayName, agent, hideAfterTimeout);
122
123
if (openNativeElementsPanel) {
124
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0 = node;
@@ -171,7 +171,7 @@ export default function setupHighlighter(
171
172
// Don't pass the name explicitly.
173
// It will be inferred from DOM tag and Fiber owner.
174
- showOverlay([target], null, false);
174
+ showOverlay([target], null, agent, false);
175
176
selectFiberForNode(target);
177
}