Skip to content

Commit

Permalink
Rename getNearestMountedHostInstance to getNearestMountedDOMNode
Browse files Browse the repository at this point in the history
This conflict resolution is really only relevant to DOM renderers since
there's generally multiple of them per tree and this resolution is only
active for those anyway. Making this clear means we can change the
implementation details.
  • Loading branch information
sebmarkbage committed Aug 28, 2024
1 parent 8619997 commit 2ec16d4
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 82 deletions.
165 changes: 102 additions & 63 deletions packages/react-devtools-shared/src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,84 +341,123 @@ export default class Agent extends EventEmitter<{
}

getIDForHostInstance(target: HostInstance): number | null {
let bestMatch: null | HostInstance = null;
let bestRenderer: null | RendererInterface = null;
// Find the nearest ancestor which is mounted by a React.
for (const rendererID in this._rendererInterfaces) {
const renderer = ((this._rendererInterfaces[
(rendererID: any)
]: any): RendererInterface);
const nearestNode: null = renderer.getNearestMountedHostInstance(target);
if (nearestNode !== null) {
if (nearestNode === target) {
// Exact match we can exit early.
bestMatch = nearestNode;
bestRenderer = renderer;
break;
if (isReactNativeEnvironment() || typeof target.nodeType !== 'number') {
// In React Native or non-DOM we simply pick any renderer that has a match.
for (const rendererID in this._rendererInterfaces) {
const renderer = ((this._rendererInterfaces[
(rendererID: any)
]: any): RendererInterface);
try {
const match = renderer.getElementIDForHostInstance(target);
if (match != null) {
return match;
}
} catch (error) {
// Some old React versions might throw if they can't find a match.
// If so we should ignore it...
}
if (
bestMatch === null ||
(!isReactNativeEnvironment() && bestMatch.contains(nearestNode))
) {
// If this is the first match or the previous match contains the new match,
// so the new match is a deeper and therefore better match.
bestMatch = nearestNode;
bestRenderer = renderer;
}
return null;
} else {
// In the DOM we use a smarter mechanism to find the deepest a DOM node
// that is registered if there isn't an exact match.
let bestMatch: null | Element = null;
let bestRenderer: null | RendererInterface = null;
// Find the nearest ancestor which is mounted by a React.
for (const rendererID in this._rendererInterfaces) {
const renderer = ((this._rendererInterfaces[
(rendererID: any)
]: any): RendererInterface);
const nearestNode: null | Element = renderer.getNearestMountedDOMNode(
(target: any),
);
if (nearestNode !== null) {
if (nearestNode === target) {
// Exact match we can exit early.
bestMatch = nearestNode;
bestRenderer = renderer;
break;
}
if (bestMatch === null || bestMatch.contains(nearestNode)) {
// If this is the first match or the previous match contains the new match,
// so the new match is a deeper and therefore better match.
bestMatch = nearestNode;
bestRenderer = renderer;
}
}
}
}
if (bestRenderer != null && bestMatch != null) {
try {
return bestRenderer.getElementIDForHostInstance(bestMatch, true);
} catch (error) {
// Some old React versions might throw if they can't find a match.
// If so we should ignore it...
if (bestRenderer != null && bestMatch != null) {
try {
return bestRenderer.getElementIDForHostInstance(bestMatch);
} catch (error) {
// Some old React versions might throw if they can't find a match.
// If so we should ignore it...
}
}
return null;
}
return null;
}

getComponentNameForHostInstance(target: HostInstance): string | null {
// We duplicate this code from getIDForHostInstance to avoid an object allocation.
let bestMatch: null | HostInstance = null;
let bestRenderer: null | RendererInterface = null;
// Find the nearest ancestor which is mounted by a React.
for (const rendererID in this._rendererInterfaces) {
const renderer = ((this._rendererInterfaces[
(rendererID: any)
]: any): RendererInterface);
const nearestNode = renderer.getNearestMountedHostInstance(target);
if (nearestNode !== null) {
if (nearestNode === target) {
// Exact match we can exit early.
bestMatch = nearestNode;
bestRenderer = renderer;
break;
if (isReactNativeEnvironment() || typeof target.nodeType !== 'number') {
// In React Native or non-DOM we simply pick any renderer that has a match.
for (const rendererID in this._rendererInterfaces) {
const renderer = ((this._rendererInterfaces[
(rendererID: any)
]: any): RendererInterface);
try {
const id = renderer.getElementIDForHostInstance(target);
if (id) {
return renderer.getDisplayNameForElementID(id);
}
} catch (error) {
// Some old React versions might throw if they can't find a match.
// If so we should ignore it...
}
if (
bestMatch === null ||
(!isReactNativeEnvironment() && bestMatch.contains(nearestNode))
) {
// If this is the first match or the previous match contains the new match,
// so the new match is a deeper and therefore better match.
bestMatch = nearestNode;
bestRenderer = renderer;
}
return null;
} else {
// In the DOM we use a smarter mechanism to find the deepest a DOM node
// that is registered if there isn't an exact match.
let bestMatch: null | Element = null;
let bestRenderer: null | RendererInterface = null;
// Find the nearest ancestor which is mounted by a React.
for (const rendererID in this._rendererInterfaces) {
const renderer = ((this._rendererInterfaces[
(rendererID: any)
]: any): RendererInterface);
const nearestNode: null | Element = renderer.getNearestMountedDOMNode(
(target: any),
);
if (nearestNode !== null) {
if (nearestNode === target) {
// Exact match we can exit early.
bestMatch = nearestNode;
bestRenderer = renderer;
break;
}
if (bestMatch === null || bestMatch.contains(nearestNode)) {
// If this is the first match or the previous match contains the new match,
// so the new match is a deeper and therefore better match.
bestMatch = nearestNode;
bestRenderer = renderer;
}
}
}
}

if (bestRenderer != null && bestMatch != null) {
try {
const id = bestRenderer.getElementIDForHostInstance(bestMatch, true);
if (id) {
return bestRenderer.getDisplayNameForElementID(id);
if (bestRenderer != null && bestMatch != null) {
try {
const id = bestRenderer.getElementIDForHostInstance(bestMatch);
if (id) {
return bestRenderer.getDisplayNameForElementID(id);
}
} catch (error) {
// Some old React versions might throw if they can't find a match.
// If so we should ignore it...
}
} catch (error) {
// Some old React versions might throw if they can't find a match.
// If so we should ignore it...
}
return null;
}
return null;
}

getBackendVersion: () => void = () => {
Expand Down
16 changes: 10 additions & 6 deletions packages/react-devtools-shared/src/backend/fiber/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -804,11 +804,17 @@ function releaseHostResource(
if (resourceInstances.size === 0) {
hostResourceToDevToolsInstanceMap.delete(publicInstance);
publicInstanceToDevToolsInstanceMap.delete(publicInstance);
} else if (publicInstanceToDevToolsInstanceMap.get(publicInstance) === nearestInstance) {
} else if (
publicInstanceToDevToolsInstanceMap.get(publicInstance) ===
nearestInstance
) {
// This was the first one. Store the next first one in the main map for easy access.
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
for (const firstInstance of resourceInstances) {
publicInstanceToDevToolsInstanceMap.set(firstInstance, nearestInstance);
publicInstanceToDevToolsInstanceMap.set(
firstInstance,
nearestInstance,
);
break;
}
}
Expand Down Expand Up @@ -3718,9 +3724,7 @@ export function attach(
}
}

function getNearestMountedHostInstance(
publicInstance: HostInstance,
): null | HostInstance {
function getNearestMountedDOMNode(publicInstance: Element): null | Element {
// TODO: Remove dependency on findFiberByHostInstance.
const mountedFiber = renderer.findFiberByHostInstance(publicInstance);
if (mountedFiber != null) {
Expand Down Expand Up @@ -5614,7 +5618,7 @@ export function attach(
flushInitialOperations,
getBestMatchForTrackedPath,
getDisplayNameForElementID,
getNearestMountedHostInstance,
getNearestMountedDOMNode,
getElementIDForHostInstance,
getInstanceAndStyle,
getOwnersList,
Expand Down
14 changes: 5 additions & 9 deletions packages/react-devtools-shared/src/backend/legacy/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,13 @@ export function attach(
let getElementIDForHostInstance: GetElementIDForHostInstance =
((null: any): GetElementIDForHostInstance);
let findHostInstanceForInternalID: (id: number) => ?HostInstance;
let getNearestMountedHostInstance = (
node: HostInstance,
): null | HostInstance => {
let getNearestMountedDOMNode = (node: Element): null | Element => {
// Not implemented.
return null;
};

if (renderer.ComponentTree) {
getElementIDForHostInstance = (node, findNearestUnfilteredAncestor) => {
getElementIDForHostInstance = node => {
const internalInstance =
renderer.ComponentTree.getClosestInstanceFromNode(node);
return internalInstanceToIDMap.get(internalInstance) || null;
Expand All @@ -162,9 +160,7 @@ export function attach(
const internalInstance = idToInternalInstanceMap.get(id);
return renderer.ComponentTree.getNodeFromInstance(internalInstance);
};
getNearestMountedHostInstance = (
node: HostInstance,
): null | HostInstance => {
getNearestMountedDOMNode = (node: Element): null | Element => {
const internalInstance =
renderer.ComponentTree.getClosestInstanceFromNode(node);
if (internalInstance != null) {
Expand All @@ -173,7 +169,7 @@ export function attach(
return null;
};
} else if (renderer.Mount.getID && renderer.Mount.getNode) {
getElementIDForHostInstance = (node, findNearestUnfilteredAncestor) => {
getElementIDForHostInstance = node => {
// Not implemented.
return null;
};
Expand Down Expand Up @@ -1121,7 +1117,7 @@ export function attach(
flushInitialOperations,
getBestMatchForTrackedPath,
getDisplayNameForElementID,
getNearestMountedHostInstance,
getNearestMountedDOMNode,
getElementIDForHostInstance,
getInstanceAndStyle,
findHostInstancesForElementID: (id: number) => {
Expand Down
5 changes: 1 addition & 4 deletions packages/react-devtools-shared/src/backend/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ export type GetDisplayNameForElementID = (id: number) => string | null;

export type GetElementIDForHostInstance = (
component: HostInstance,
findNearestUnfilteredAncestor?: boolean,
) => number | null;
export type FindHostInstancesForElementID = (
id: number,
Expand Down Expand Up @@ -360,9 +359,7 @@ export type RendererInterface = {
findHostInstancesForElementID: FindHostInstancesForElementID,
flushInitialOperations: () => void,
getBestMatchForTrackedPath: () => PathMatch | null,
getNearestMountedHostInstance: (
component: HostInstance,
) => HostInstance | null,
getNearestMountedDOMNode: (component: Element) => Element | null,
getElementIDForHostInstance: GetElementIDForHostInstance,
getDisplayNameForElementID: GetDisplayNameForElementID,
getInstanceAndStyle(id: number): InstanceAndStyle,
Expand Down

0 comments on commit 2ec16d4

Please sign in to comment.