diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index 75cf2c7c03755..50ee8e74dabf7 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -2695,11 +2695,11 @@ export function attach( // If we're tracing updates and we've bailed out before reaching a host node, // we should fall back to recursively marking the nearest host descendants for highlight. if (traceNearestHostComponentUpdate) { - const hostFibers = findAllCurrentHostFibers( + const hostInstances = findAllCurrentHostInstances( getFiberInstanceThrows(nextFiber), ); - hostFibers.forEach(hostFiber => { - traceUpdatesForNodes.add(hostFiber.stateNode); + hostInstances.forEach(hostInstance => { + traceUpdatesForNodes.add(hostInstance); }); } } @@ -2943,31 +2943,54 @@ export function attach( currentRootID = -1; } - function findAllCurrentHostFibers( + function getResourceInstance(fiber: Fiber): HostInstance | null { + if (fiber.tag === HostHoistable) { + const resource = fiber.memoizedState; + // Feature Detect a DOM Specific Instance of a Resource + if ( + typeof resource === 'object' && + resource !== null && + resource.instance != null + ) { + return resource.instance; + } + } + return null; + } + + function findAllCurrentHostInstances( fiberInstance: FiberInstance, - ): $ReadOnlyArray { - const fibers = []; + ): $ReadOnlyArray { + const hostInstances = []; const fiber = findCurrentFiberUsingSlowPathByFiberInstance(fiberInstance); if (!fiber) { - return fibers; + return hostInstances; } // Next we'll drill down this component to find all HostComponent/Text. let node: Fiber = fiber; while (true) { - if (node.tag === HostComponent || node.tag === HostText) { - fibers.push(node); + if ( + node.tag === HostComponent || + node.tag === HostText || + node.tag === HostSingleton || + node.tag === HostHoistable + ) { + const hostInstance = node.stateNode || getResourceInstance(node); + if (hostInstance) { + hostInstances.push(hostInstance); + } } else if (node.child) { node.child.return = node; node = node.child; continue; } if (node === fiber) { - return fibers; + return hostInstances; } while (!node.sibling) { if (!node.return || node.return === fiber) { - return fibers; + return hostInstances; } node = node.return; } @@ -2976,7 +2999,7 @@ export function attach( } // Flow needs the return here, but ESLint complains about it. // eslint-disable-next-line no-unreachable - return fibers; + return hostInstances; } function findHostInstancesForElementID(id: number) { @@ -2996,8 +3019,8 @@ export function attach( return null; } - const hostFibers = findAllCurrentHostFibers(devtoolsInstance); - return hostFibers.map(hostFiber => hostFiber.stateNode).filter(Boolean); + const hostInstances = findAllCurrentHostInstances(devtoolsInstance); + return hostInstances; } catch (err) { // The fiber might have unmounted by now. return null; diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index fdcfddea5fb6b..2bd13a3a1294d 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -94,7 +94,7 @@ export type GetElementIDForHostInstance = ( ) => number | null; export type FindHostInstancesForElementID = ( id: number, -) => ?Array; +) => null | $ReadOnlyArray; export type ReactProviderType = { $$typeof: symbol | number, diff --git a/packages/react-devtools-shared/src/backend/views/Highlighter/Highlighter.js b/packages/react-devtools-shared/src/backend/views/Highlighter/Highlighter.js index 6ed083abe4c1b..ddcb6f1ef11a9 100644 --- a/packages/react-devtools-shared/src/backend/views/Highlighter/Highlighter.js +++ b/packages/react-devtools-shared/src/backend/views/Highlighter/Highlighter.js @@ -38,12 +38,15 @@ export function hideOverlay(agent: Agent): void { : hideOverlayWeb(); } -function showOverlayNative(elements: Array, agent: Agent): void { +function showOverlayNative( + elements: $ReadOnlyArray, + agent: Agent, +): void { agent.emit('showNativeHighlight', elements); } function showOverlayWeb( - elements: Array, + elements: $ReadOnlyArray, componentName: string | null, agent: Agent, hideAfterTimeout: boolean, @@ -64,12 +67,17 @@ function showOverlayWeb( } export function showOverlay( - elements: Array, + elements: $ReadOnlyArray, componentName: string | null, agent: Agent, hideAfterTimeout: boolean, ): void { return isReactNativeEnvironment() ? showOverlayNative(elements, agent) - : showOverlayWeb((elements: any), componentName, agent, hideAfterTimeout); + : showOverlayWeb( + (elements: $ReadOnlyArray), + componentName, + agent, + hideAfterTimeout, + ); } diff --git a/packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js b/packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js index fe0a40d8e9c2d..cdaf64ed8c7a3 100644 --- a/packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js +++ b/packages/react-devtools-shared/src/backend/views/Highlighter/Overlay.js @@ -187,7 +187,7 @@ export default class Overlay { } } - inspect(nodes: Array, name?: ?string) { + inspect(nodes: $ReadOnlyArray, name?: ?string) { // We can't get the size of text nodes or comment nodes. React as of v15 // heavily uses comment nodes to delimit text. const elements = nodes.filter(node => node.nodeType === Node.ELEMENT_NODE); diff --git a/packages/react-devtools-shared/src/backend/views/Highlighter/index.js b/packages/react-devtools-shared/src/backend/views/Highlighter/index.js index dc711d7881bd4..7fefa837e2fc5 100644 --- a/packages/react-devtools-shared/src/backend/views/Highlighter/index.js +++ b/packages/react-devtools-shared/src/backend/views/Highlighter/index.js @@ -13,7 +13,6 @@ import Agent from 'react-devtools-shared/src/backend/agent'; import {hideOverlay, showOverlay} from './Highlighter'; import type {BackendBridge} from 'react-devtools-shared/src/bridge'; -import type {HostInstance} from '../../types'; // This plug-in provides in-page highlighting of the selected element. // It is used by the browser extension and the standalone DevTools shell (when connected to a browser). @@ -113,8 +112,7 @@ export default function setupHighlighter( return; } - const nodes: ?Array = - renderer.findHostInstancesForElementID(id); + const nodes = renderer.findHostInstancesForElementID(id); if (nodes != null && nodes[0] != null) { const node = nodes[0];