From ea3efa80a8ff0aaceb90e3828a8739454a98de1e Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 4 Aug 2023 03:23:28 -0700 Subject: [PATCH] fix[AppContainer]: mount react devtools overlay only when devtools are connected (#38727) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/38727 ## Changelog: [General][Fix] - Do not render React DevTools overlays unless they are connected Fixes https://github.com/facebook/react-native/issues/38024. Reproducible example: https://github.com/rasaha91/rn-bottomsheet. - This is a temporary workaround to resolve described problem for DEV bundles without attached React DevTools. - Still, such problem will be present for DEV bundles with attached React DevTools, but this should be only for brownfield apps with a shrinked React Native window. Checking that DevTools hook is present is not enough to determine whether the DevTools are connected. These changes fix it. Short description of what's going on there: 1. When React Native root window is rendered inside some native view, which takes only portion of the screen (like the native bottom sheet in the reproducible example), DevtoolsOverlay / InspectorOverlay takes up space based on application window dimension, this results into resizing the hosting window on the native side. https://pxl.cl/357r3 2. Right way to fix this would be removing the usage of application window sizes, so that DevtoolsOverlay / InspectorOverlay will be allowed only to take React Native's window. 3. Unfortunately, just removing setting is not enough, we should also have at least 1 of 2 things: - `collapsable` prop should be set to `true` => View will be flattened - Remove [`flex: 1` style on both root and inner Views](https://github.com/facebook/react-native/blob/b28e3c16ed7cbc8b3ed3f26d91c58acb4bb28879/packages/react-native/Libraries/ReactNative/AppContainer.js#L145-L147), but this is breaking how LogBox works now. | {F1062478964} | {F1062492367} Reviewed By: NickGerleman Differential Revision: D47954883 fbshipit-source-id: d45d8cb82daa8dc9de58f54c137815b3a7abd5db --- .../Libraries/ReactNative/AppContainer.js | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/packages/react-native/Libraries/ReactNative/AppContainer.js b/packages/react-native/Libraries/ReactNative/AppContainer.js index 59c0cb117c36e0..feafbba6772c68 100644 --- a/packages/react-native/Libraries/ReactNative/AppContainer.js +++ b/packages/react-native/Libraries/ReactNative/AppContainer.js @@ -17,6 +17,8 @@ import {type EventSubscription} from '../vendor/emitter/EventEmitter'; import {RootTagContext, createRootTag} from './RootTag'; import * as React from 'react'; +const reactDevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__; + type Props = $ReadOnly<{| children?: React.Node, fabric?: boolean, @@ -47,9 +49,21 @@ class AppContainer extends React.Component { }; _mainRef: ?React.ElementRef; _subscription: ?EventSubscription = null; + _reactDevToolsAgentListener: ?() => void = null; static getDerivedStateFromError: any = undefined; + mountReactDevToolsOverlays(): void { + const DevtoolsOverlay = require('../Inspector/DevtoolsOverlay').default; + const devtoolsOverlay = ; + + const TraceUpdateOverlay = + require('../Components/TraceUpdateOverlay/TraceUpdateOverlay').default; + const traceUpdateOverlay = ; + + this.setState({devtoolsOverlay, traceUpdateOverlay}); + } + componentDidMount(): void { if (__DEV__) { if (!this.props.internal_excludeInspector) { @@ -71,16 +85,21 @@ class AppContainer extends React.Component { this.setState({inspector}); }, ); - if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__ != null) { - const DevtoolsOverlay = - require('../Inspector/DevtoolsOverlay').default; - const devtoolsOverlay = ( - + + if (reactDevToolsHook != null) { + if (reactDevToolsHook.reactDevtoolsAgent) { + // In case if this is not the first AppContainer rendered and React DevTools are already attached + this.mountReactDevToolsOverlays(); + return; + } + + this._reactDevToolsAgentListener = () => + this.mountReactDevToolsOverlays(); + + reactDevToolsHook.on( + 'react-devtools', + this._reactDevToolsAgentListener, ); - const TraceUpdateOverlay = - require('../Components/TraceUpdateOverlay/TraceUpdateOverlay').default; - const traceUpdateOverlay = ; - this.setState({devtoolsOverlay, traceUpdateOverlay}); } } } @@ -90,6 +109,10 @@ class AppContainer extends React.Component { if (this._subscription != null) { this._subscription.remove(); } + + if (reactDevToolsHook != null && this._reactDevToolsAgentListener != null) { + reactDevToolsHook.off('react-devtools', this._reactDevToolsAgentListener); + } } render(): React.Node {