@samitouri / QOS-React-2 / commits / 3e92eb0fce

[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 }