Skip to content

Commit

Permalink
[DevTools] find best renderer when inspecting (#24665)
Browse files Browse the repository at this point in the history
* [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
  • Loading branch information
mondaychen committed Jun 8, 2022
1 parent 42b330c commit 3e92eb0
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 27 deletions.
24 changes: 19 additions & 5 deletions packages/react-devtools-shared/src/backend/agent.js
Expand Up @@ -309,17 +309,31 @@ export default class Agent extends EventEmitter<{|
return renderer.getInstanceAndStyle(id);
}

getIDForNode(node: Object): number | null {
getBestMatchingRendererInterface(node: Object): RendererInterface | null {
let bestMatch = null;
for (const rendererID in this._rendererInterfaces) {
const renderer = ((this._rendererInterfaces[
(rendererID: any)
]: any): RendererInterface);
const fiber = renderer.getFiberForNative(node);
if (fiber !== null) {
// check if fiber.stateNode is matching the original hostInstance
if (fiber.stateNode === node) {
return renderer;
} else if (bestMatch === null) {
bestMatch = renderer;
}
}
}
// if an exact match is not found, return the first valid renderer as fallback
return bestMatch;
}

getIDForNode(node: Object): number | null {
const rendererInterface = this.getBestMatchingRendererInterface(node);
if (rendererInterface != null) {
try {
const id = renderer.getFiberIDForNative(node, true);
if (id !== null) {
return id;
}
return rendererInterface.getFiberIDForNative(node, true);
} catch (error) {
// Some old React versions might throw if they can't find a match.
// If so we should ignore it...
Expand Down
8 changes: 8 additions & 0 deletions packages/react-devtools-shared/src/backend/legacy/renderer.js
Expand Up @@ -148,6 +148,10 @@ export function attach(

let getInternalIDForNative: GetFiberIDForNative = ((null: any): GetFiberIDForNative);
let findNativeNodeForInternalID: (id: number) => ?NativeType;
let getFiberForNative = (node: NativeType) => {
// Not implemented.
return null;
};

if (renderer.ComponentTree) {
getInternalIDForNative = (node, findNearestUnfilteredAncestor) => {
Expand All @@ -160,6 +164,9 @@ export function attach(
const internalInstance = idToInternalInstanceMap.get(id);
return renderer.ComponentTree.getNodeFromInstance(internalInstance);
};
getFiberForNative = (node: NativeType) => {
return renderer.ComponentTree.getClosestInstanceFromNode(node);
};
} else if (renderer.Mount.getID && renderer.Mount.getNode) {
getInternalIDForNative = (node, findNearestUnfilteredAncestor) => {
// Not implemented.
Expand Down Expand Up @@ -1094,6 +1101,7 @@ export function attach(
flushInitialOperations,
getBestMatchForTrackedPath,
getDisplayNameForFiberID,
getFiberForNative,
getFiberIDForNative: getInternalIDForNative,
getInstanceAndStyle,
findNativeNodesForFiberID: (id: number) => {
Expand Down
5 changes: 5 additions & 0 deletions packages/react-devtools-shared/src/backend/renderer.js
Expand Up @@ -2818,6 +2818,10 @@ export function attach(
return fiber != null ? getDisplayNameForFiber(((fiber: any): Fiber)) : null;
}

function getFiberForNative(hostInstance) {
return renderer.findFiberByHostInstance(hostInstance);
}

function getFiberIDForNative(
hostInstance,
findNearestUnfilteredAncestor = false,
Expand Down Expand Up @@ -4490,6 +4494,7 @@ export function attach(
flushInitialOperations,
getBestMatchForTrackedPath,
getDisplayNameForFiberID,
getFiberForNative,
getFiberIDForNative,
getInstanceAndStyle,
getOwnersList,
Expand Down
3 changes: 2 additions & 1 deletion packages/react-devtools-shared/src/backend/types.js
Expand Up @@ -93,7 +93,7 @@ export type Lane = number;
export type Lanes = number;

export type ReactRenderer = {
findFiberByHostInstance: (hostInstance: NativeType) => ?Fiber,
findFiberByHostInstance: (hostInstance: NativeType) => Fiber | null,
version: string,
rendererPackageName: string,
bundleType: BundleType,
Expand Down Expand Up @@ -350,6 +350,7 @@ export type RendererInterface = {
findNativeNodesForFiberID: FindNativeNodesForFiberID,
flushInitialOperations: () => void,
getBestMatchForTrackedPath: () => PathMatch | null,
getFiberForNative: (component: NativeType) => Fiber | null,
getFiberIDForNative: GetFiberIDForNative,
getDisplayNameForFiberID: GetDisplayNameForFiberID,
getInstanceAndStyle(id: number): InstanceAndStyle,
Expand Down
Expand Up @@ -7,6 +7,8 @@
* @flow
*/

import type Agent from 'react-devtools-shared/src/backend/agent';

import Overlay from './Overlay';

const SHOW_DURATION = 2000;
Expand All @@ -26,6 +28,7 @@ export function hideOverlay() {
export function showOverlay(
elements: Array<HTMLElement> | null,
componentName: string | null,
agent: Agent,
hideAfterTimeout: boolean,
) {
// TODO (npm-packages) Detect RN and support it somehow
Expand All @@ -42,7 +45,7 @@ export function showOverlay(
}

if (overlay === null) {
overlay = new Overlay();
overlay = new Overlay(agent);
}

overlay.inspect(elements, componentName);
Expand Down
Expand Up @@ -9,13 +9,13 @@

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

const assign = Object.assign;

import type {DevToolsHook} from 'react-devtools-shared/src/backend/types';
import type {Rect} from '../utils';
import type Agent from 'react-devtools-shared/src/backend/agent';

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

const assign = Object.assign;

// Note that the Overlay components are not affected by the active Theme,
// because they highlight elements in the main Chrome window (outside of devtools).
// The colors below were chosen to roughly match those used by Chrome devtools.
Expand Down Expand Up @@ -153,8 +153,9 @@ export default class Overlay {
container: HTMLElement;
tip: OverlayTip;
rects: Array<OverlayRect>;
agent: Agent;

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

this.agent = agent;

doc.body.appendChild(this.container);
}

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

const node = elements[0];
const hook: DevToolsHook =
node.ownerDocument.defaultView.__REACT_DEVTOOLS_GLOBAL_HOOK__;
if (hook != null && hook.rendererInterfaces != null) {
let ownerName = null;
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
for (const rendererInterface of hook.rendererInterfaces.values()) {
const id = rendererInterface.getFiberIDForNative(node, true);
if (id !== null) {
ownerName = rendererInterface.getDisplayNameForFiberID(id, true);
break;
const rendererInterface = this.agent.getBestMatchingRendererInterface(
node,
);
if (rendererInterface) {
const id = rendererInterface.getFiberIDForNative(node, true);
if (id) {
const ownerName = rendererInterface.getDisplayNameForFiberID(
id,
true,
);
if (ownerName) {
name += ' (in ' + ownerName + ')';
}
}

if (ownerName) {
name += ' (in ' + ownerName + ')';
}
}
}

Expand Down
Expand Up @@ -118,7 +118,7 @@ export default function setupHighlighter(
node.scrollIntoView({block: 'nearest', inline: 'nearest'});
}

showOverlay(nodes, displayName, hideAfterTimeout);
showOverlay(nodes, displayName, agent, hideAfterTimeout);

if (openNativeElementsPanel) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0 = node;
Expand Down Expand Up @@ -171,7 +171,7 @@ export default function setupHighlighter(

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

selectFiberForNode(target);
}
Expand Down

0 comments on commit 3e92eb0

Please sign in to comment.