/** * 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. * * @flow strict-local * @format * @oncall react_native */ import type { ReactDevToolsAgent, ReactDevToolsGlobalHook, } from '../Types/ReactDevToolsTypes'; import type {Props} from './AppContainer'; import ReactNativeStyleAttributes from '../Components/View/ReactNativeStyleAttributes'; import View from '../Components/View/View'; import DebuggingOverlay from '../Debugging/DebuggingOverlay'; import useSubscribeToDebuggingOverlayRegistry from '../Debugging/useSubscribeToDebuggingOverlayRegistry'; import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter'; import LogBoxNotificationContainer from '../LogBox/LogBoxNotificationContainer'; import StyleSheet from '../StyleSheet/StyleSheet'; import {RootTagContext, createRootTag} from './RootTag'; import * as React from 'react'; const {useEffect, useState, useCallback} = React; const reactDevToolsHook: ReactDevToolsGlobalHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__; // Required for React DevTools to view / edit React Native styles in Flipper. // Flipper doesn't inject these values when initializing DevTools. if (reactDevToolsHook) { reactDevToolsHook.resolveRNStyle = require('../StyleSheet/flattenStyle'); reactDevToolsHook.nativeStyleEditorValidAttributes = Object.keys( ReactNativeStyleAttributes, ); } type InspectorDeferredProps = { inspectedViewRef: InspectedViewRef, onInspectedViewRerenderRequest: () => void, reactDevToolsAgent?: ReactDevToolsAgent, }; const InspectorDeferred = ({ inspectedViewRef, onInspectedViewRerenderRequest, reactDevToolsAgent, }: InspectorDeferredProps) => { // D39382967 adds a require cycle: InitializeCore -> AppContainer -> Inspector -> InspectorPanel -> ScrollView -> InitializeCore // We can't remove it yet, fallback to dynamic require for now. This is the only reason why this logic is in a separate function. const Inspector = require('../Inspector/Inspector'); return ( ); }; type ReactDevToolsOverlayDeferredProps = { inspectedViewRef: InspectedViewRef, reactDevToolsAgent: ReactDevToolsAgent, }; const ReactDevToolsOverlayDeferred = ({ inspectedViewRef, reactDevToolsAgent, }: ReactDevToolsOverlayDeferredProps) => { const ReactDevToolsOverlay = require('../Inspector/ReactDevToolsOverlay').default; return ( ); }; const AppContainer = ({ children, fabric, initialProps, internal_excludeInspector = false, internal_excludeLogBox = false, rootTag, WrapperComponent, rootViewStyle, }: Props): React.Node => { const appContainerRootViewRef: AppContainerRootViewRef = React.useRef(null); const innerViewRef: InspectedViewRef = React.useRef(null); const debuggingOverlayRef: DebuggingOverlayRef = React.useRef(null); useSubscribeToDebuggingOverlayRegistry( appContainerRootViewRef, debuggingOverlayRef, ); const [key, setKey] = useState(0); const [shouldRenderInspector, setShouldRenderInspector] = useState(false); const [reactDevToolsAgent, setReactDevToolsAgent] = useState(reactDevToolsHook?.reactDevtoolsAgent); useEffect(() => { let inspectorSubscription = null; if (!internal_excludeInspector) { inspectorSubscription = RCTDeviceEventEmitter.addListener( 'toggleElementInspector', () => setShouldRenderInspector(value => !value), ); } let reactDevToolsAgentListener = null; // If this is first render, subscribe to the event from React DevTools hook if (reactDevToolsHook != null && reactDevToolsAgent == null) { reactDevToolsAgentListener = setReactDevToolsAgent; reactDevToolsHook.on?.('react-devtools', reactDevToolsAgentListener); } return () => { inspectorSubscription?.remove(); if ( reactDevToolsHook?.off != null && reactDevToolsAgentListener != null ) { reactDevToolsHook.off('react-devtools', reactDevToolsAgentListener); } }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); let innerView: React.Node = ( {children} ); if (WrapperComponent != null) { innerView = ( {innerView} ); } const onInspectedViewRerenderRequest = useCallback( () => setKey(k => k + 1), [], ); return ( {innerView} {reactDevToolsAgent != null && ( )} {shouldRenderInspector && ( )} {!internal_excludeLogBox && } ); }; const styles = StyleSheet.create({ container: {flex: 1}, }); export type AppContainerRootViewRef = React.RefObject | null>; export type InspectedViewRef = React.RefObject | null>; export type DebuggingOverlayRef = React.RefObject | null>; export default AppContainer;