diff --git a/Libraries/Inspector/DevtoolsHighlighter.js b/Libraries/Inspector/DevtoolsHighlighter.js
new file mode 100644
index 00000000000000..c3d6f1c3a3fbc3
--- /dev/null
+++ b/Libraries/Inspector/DevtoolsHighlighter.js
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @flow
+ */
+
+import ElementBox from './ElementBox';
+import * as React from 'react';
+const {useEffect, useState} = React;
+
+const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
+
+export default function DevtoolsHighlighter(): React.Node {
+ const [inspected, setInspected] = useState(null);
+ useEffect(() => {
+ let devToolsAgent = null;
+ let hideTimeoutId = null;
+
+ function onAgentHideNativeHighlight() {
+ // we wait to actually hide in order to avoid flicker
+ clearTimeout(hideTimeoutId);
+ hideTimeoutId = setTimeout(() => {
+ setInspected(null);
+ }, 100);
+ }
+
+ function onAgentShowNativeHighlight(node: any) {
+ clearTimeout(hideTimeoutId);
+ // Shape of `node` is different in Fabric.
+ const component = node.canonical ?? node;
+
+ component.measure((x, y, width, height, left, top) => {
+ setInspected({
+ frame: {left, top, width, height},
+ });
+ });
+ }
+
+ function cleanup() {
+ const currentAgent = devToolsAgent;
+ if (currentAgent != null) {
+ currentAgent.removeListener(
+ 'hideNativeHighlight',
+ onAgentHideNativeHighlight,
+ );
+ currentAgent.removeListener(
+ 'showNativeHighlight',
+ onAgentShowNativeHighlight,
+ );
+ currentAgent.removeListener('shutdown', cleanup);
+ devToolsAgent = null;
+ }
+ }
+
+ function _attachToDevtools(agent: Object) {
+ devToolsAgent = agent;
+ agent.addListener('hideNativeHighlight', onAgentHideNativeHighlight);
+ agent.addListener('showNativeHighlight', onAgentShowNativeHighlight);
+ agent.addListener('shutdown', cleanup);
+ }
+
+ hook.on('react-devtools', _attachToDevtools);
+ if (hook.reactDevtoolsAgent) {
+ _attachToDevtools(hook.reactDevtoolsAgent);
+ }
+ return () => {
+ hook.off('react-devtools', _attachToDevtools);
+ cleanup();
+ };
+ }, []);
+
+ if (inspected != null) {
+ return ;
+ }
+ return null;
+}
diff --git a/Libraries/Inspector/Inspector.js b/Libraries/Inspector/Inspector.js
index 1134e25429ff35..504c6963f5386a 100644
--- a/Libraries/Inspector/Inspector.js
+++ b/Libraries/Inspector/Inspector.js
@@ -144,8 +144,6 @@ class Inspector extends React.Component<
}
_attachToDevtools = (agent: Object) => {
- agent.addListener('hideNativeHighlight', this._onAgentHideNativeHighlight);
- agent.addListener('showNativeHighlight', this._onAgentShowNativeHighlight);
agent.addListener('shutdown', this._onAgentShutdown);
this.setState({
@@ -153,45 +151,9 @@ class Inspector extends React.Component<
});
};
- _onAgentHideNativeHighlight = () => {
- if (this.state.inspected === null) {
- return;
- }
- // we wait to actually hide in order to avoid flicker
- this._hideTimeoutID = setTimeout(() => {
- this.setState({
- inspected: null,
- });
- }, 100);
- };
-
- _onAgentShowNativeHighlight = (node: any) => {
- clearTimeout(this._hideTimeoutID);
-
- // Shape of `node` is different in Fabric.
- const component = node.canonical ?? node;
-
- component.measure((x, y, width, height, left, top) => {
- this.setState({
- hierarchy: [],
- inspected: {
- frame: {left, top, width, height},
- },
- });
- });
- };
-
_onAgentShutdown = () => {
const agent = this.state.devtoolsAgent;
if (agent != null) {
- agent.removeListener(
- 'hideNativeHighlight',
- this._onAgentHideNativeHighlight,
- );
- agent.removeListener(
- 'showNativeHighlight',
- this._onAgentShowNativeHighlight,
- );
agent.removeListener('shutdown', this._onAgentShutdown);
this.setState({devtoolsAgent: null});
diff --git a/Libraries/ReactNative/AppContainer.js b/Libraries/ReactNative/AppContainer.js
index 8495893f8e3186..ccf04260152b34 100644
--- a/Libraries/ReactNative/AppContainer.js
+++ b/Libraries/ReactNative/AppContainer.js
@@ -77,14 +77,19 @@ class AppContainer extends React.Component {
render(): React.Node {
let logBox = null;
+ let devtoolsHighlighter = null;
if (__DEV__) {
- if (
- !global.__RCTProfileIsProfiling &&
- !this.props.internal_excludeLogBox
- ) {
- const LogBoxNotificationContainer =
- require('../LogBox/LogBoxNotificationContainer').default;
- logBox = ;
+ if (!global.__RCTProfileIsProfiling) {
+ if (!this.props.internal_excludeLogBox) {
+ const LogBoxNotificationContainer =
+ require('../LogBox/LogBoxNotificationContainer').default;
+ logBox = ;
+ }
+ if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__ != null) {
+ const DevtoolsHighlighter =
+ require('../Inspector/DevtoolsHighlighter').default;
+ devtoolsHighlighter = ;
+ }
}
}
@@ -118,6 +123,7 @@ class AppContainer extends React.Component {
{!this.state.hasError && innerView}
+ {devtoolsHighlighter}
{this.state.inspector}
{logBox}