diff --git a/packages/react-native/Libraries/Debugging/DebuggingRegistry.js b/packages/react-native/Libraries/Debugging/DebuggingRegistry.js new file mode 100644 index 00000000000000..9b2c8f535f1305 --- /dev/null +++ b/packages/react-native/Libraries/Debugging/DebuggingRegistry.js @@ -0,0 +1,37 @@ +/** + * 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 {AppContainerRootViewRef} from '../ReactNative/AppContainer-dev'; + +export type DebuggingRegistrySubscriberProtocol = { + rootViewRef: AppContainerRootViewRef, +}; + +class DebuggingRegistry { + #registry: Set = new Set(); + + subscribe(subscriber: DebuggingRegistrySubscriberProtocol) { + this.#registry.add(subscriber); + } + + unsubscribe(subscriber: DebuggingRegistrySubscriberProtocol) { + const wasPresent = this.#registry.delete(subscriber); + if (!wasPresent) { + console.error( + '[DebuggingRegistry] Unexpected argument for unsubscription, which was not previously subscribed:', + subscriber, + ); + } + } +} + +const debuggingRegistryInstance: DebuggingRegistry = new DebuggingRegistry(); +export default debuggingRegistryInstance; diff --git a/packages/react-native/Libraries/Debugging/useSubscribeToDebuggingRegistry.js b/packages/react-native/Libraries/Debugging/useSubscribeToDebuggingRegistry.js new file mode 100644 index 00000000000000..feb01eafce5f00 --- /dev/null +++ b/packages/react-native/Libraries/Debugging/useSubscribeToDebuggingRegistry.js @@ -0,0 +1,28 @@ +/** + * 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 {AppContainerRootViewRef} from '../ReactNative/AppContainer-dev'; + +import DebuggingRegistry from './DebuggingRegistry'; +import {useEffect} from 'react'; + +const useSubscribeToDebuggingRegistry = ( + rootViewRef: AppContainerRootViewRef, +) => { + useEffect(() => { + const subscriber = {rootViewRef}; + + DebuggingRegistry.subscribe(subscriber); + return () => DebuggingRegistry.unsubscribe(subscriber); + }, [rootViewRef]); +}; + +export default useSubscribeToDebuggingRegistry; diff --git a/packages/react-native/Libraries/Inspector/Inspector.js b/packages/react-native/Libraries/Inspector/Inspector.js index dc274521388903..94c32994a781c6 100644 --- a/packages/react-native/Libraries/Inspector/Inspector.js +++ b/packages/react-native/Libraries/Inspector/Inspector.js @@ -10,6 +10,7 @@ 'use strict'; +import type {InspectedViewRef} from '../ReactNative/AppContainer-dev'; import type { InspectorData, TouchedViewDataAtPoint, @@ -46,7 +47,7 @@ export type InspectedElement = $ReadOnly<{ export type ElementsHierarchy = InspectorData['hierarchy']; type Props = { - inspectedViewRef: React.RefObject | null>, + inspectedViewRef: InspectedViewRef, onRequestRerenderApp: () => void, reactDevToolsAgent?: ReactDevToolsAgent, }; diff --git a/packages/react-native/Libraries/Inspector/ReactDevToolsOverlay.js b/packages/react-native/Libraries/Inspector/ReactDevToolsOverlay.js index cca9e7e8b7dc88..c62bc27b2ebe17 100644 --- a/packages/react-native/Libraries/Inspector/ReactDevToolsOverlay.js +++ b/packages/react-native/Libraries/Inspector/ReactDevToolsOverlay.js @@ -8,6 +8,7 @@ * @flow */ +import type {InspectedViewRef} from '../ReactNative/AppContainer-dev'; import type {PointerEvent} from '../Types/CoreEventTypes'; import type {PressEvent} from '../Types/CoreEventTypes'; import type { @@ -29,7 +30,7 @@ const getInspectorDataForViewAtPoint = require('./getInspectorDataForViewAtPoint const {useEffect, useState, useCallback} = React; type Props = { - inspectedViewRef: React.RefObject | null>, + inspectedViewRef: InspectedViewRef, reactDevToolsAgent: ReactDevToolsAgent, }; diff --git a/packages/react-native/Libraries/ReactNative/AppContainer-dev.js b/packages/react-native/Libraries/ReactNative/AppContainer-dev.js index 3e4fe8389610e7..21171128e99379 100644 --- a/packages/react-native/Libraries/ReactNative/AppContainer-dev.js +++ b/packages/react-native/Libraries/ReactNative/AppContainer-dev.js @@ -18,6 +18,7 @@ import type {Props} from './AppContainer'; import TraceUpdateOverlay from '../Components/TraceUpdateOverlay/TraceUpdateOverlay'; import ReactNativeStyleAttributes from '../Components/View/ReactNativeStyleAttributes'; import View from '../Components/View/View'; +import useSubscribeToDebuggingRegistry from '../Debugging/useSubscribeToDebuggingRegistry'; import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter'; import ReactDevToolsOverlay from '../Inspector/ReactDevToolsOverlay'; import LogBoxNotificationContainer from '../LogBox/LogBoxNotificationContainer'; @@ -40,7 +41,7 @@ if (reactDevToolsHook) { } type InspectorDeferredProps = { - inspectedViewRef: React.RefObject | null>, + inspectedViewRef: InspectedViewRef, onInspectedViewRerenderRequest: () => void, reactDevToolsAgent?: ReactDevToolsAgent, }; @@ -73,7 +74,9 @@ const AppContainer = ({ showArchitectureIndicator, WrapperComponent, }: Props): React.Node => { - const innerViewRef = React.useRef | null>(null); + const appContainerRootViewRef: AppContainerRootViewRef = React.useRef(null); + const innerViewRef: InspectedViewRef = React.useRef(null); + useSubscribeToDebuggingRegistry(appContainerRootViewRef); const [key, setKey] = useState(0); const [shouldRenderInspector, setShouldRenderInspector] = useState(false); @@ -138,7 +141,10 @@ const AppContainer = ({ return ( - + {innerView} {reactDevToolsAgent != null && ( @@ -169,4 +175,10 @@ const styles = StyleSheet.create({ container: {flex: 1}, }); +export type AppContainerRootViewRef = React.RefObject | null>; +export type InspectedViewRef = React.RefObject | null>; export default AppContainer;