diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index d06a47e9d3..0c1acc99cb 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -808,6 +808,27 @@ function getPublicInstance(instance: HostInstance): HostInstance { return instance; } +function getNativeTag(instance: HostInstance): number | null { + if (typeof instance !== 'object' || instance === null) { + return null; + } + + // Modern. Fabric. + if ( + instance.canonical != null && + typeof instance.canonical.nativeTag === 'number' + ) { + return instance.canonical.nativeTag; + } + + // Legacy. Paper. + if (typeof instance._nativeTag === 'number') { + return instance._nativeTag; + } + + return null; +} + function aquireHostInstance( nearestInstance: DevToolsInstance, hostInstance: HostInstance, @@ -4298,6 +4319,11 @@ export function attach( componentLogsEntry = fiberToComponentLogsMap.get(fiber.alternate); } + let nativeTag = null; + if (elementType === ElementTypeHostComponent) { + nativeTag = getNativeTag(fiber.stateNode); + } + return { id: fiberInstance.id, @@ -4364,6 +4390,8 @@ export function attach( rendererVersion: renderer.version, plugins, + + nativeTag, }; } @@ -4457,6 +4485,8 @@ export function attach( rendererVersion: renderer.version, plugins, + + nativeTag: null, }; } diff --git a/packages/react-devtools-shared/src/backend/legacy/renderer.js b/packages/react-devtools-shared/src/backend/legacy/renderer.js index 8e26dcae44..cbcc37e319 100644 --- a/packages/react-devtools-shared/src/backend/legacy/renderer.js +++ b/packages/react-devtools-shared/src/backend/legacy/renderer.js @@ -859,6 +859,8 @@ export function attach( plugins: { stylex: null, }, + + nativeTag: null, }; } diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index 56fb9a8ec3..f9f11586a0 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -294,6 +294,9 @@ export type InspectedElement = { // UI plugins/visualizations for the inspected element. plugins: Plugins, + + // React Native only. + nativeTag: number | null, }; export const InspectElementErrorType = 'error'; diff --git a/packages/react-devtools-shared/src/backendAPI.js b/packages/react-devtools-shared/src/backendAPI.js index 7a8bb80144..b3668b9d98 100644 --- a/packages/react-devtools-shared/src/backendAPI.js +++ b/packages/react-devtools-shared/src/backendAPI.js @@ -239,6 +239,7 @@ export function convertInspectedElementBackendToFrontend( key, errors, warnings, + nativeTag, } = inspectedElementBackend; const inspectedElement: InspectedElementFrontend = { @@ -273,6 +274,7 @@ export function convertInspectedElementBackendToFrontend( state: hydrateHelper(state), errors, warnings, + nativeTag, }; return inspectedElement; diff --git a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementBadges.js b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementBadges.js index 9a069b484b..07e303dbc7 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementBadges.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementBadges.js @@ -11,21 +11,25 @@ import * as React from 'react'; import Badge from './Badge'; import ForgetBadge from './ForgetBadge'; +import NativeTagBadge from './NativeTagBadge'; import styles from './InspectedElementBadges.css'; type Props = { hocDisplayNames: null | Array, compiledWithForget: boolean, + nativeTag: number | null, }; export default function InspectedElementBadges({ hocDisplayNames, compiledWithForget, + nativeTag, }: Props): React.Node { if ( !compiledWithForget && - (hocDisplayNames == null || hocDisplayNames.length === 0) + (hocDisplayNames == null || hocDisplayNames.length === 0) && + nativeTag === null ) { return null; } @@ -33,6 +37,7 @@ export default function InspectedElementBadges({ return (
{compiledWithForget && } + {nativeTag !== null && } {hocDisplayNames !== null && hocDisplayNames.map(hocDisplayName => ( diff --git a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementView.js b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementView.js index 8eec411492..7e18ad5cd7 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementView.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementView.js @@ -54,8 +54,14 @@ export default function InspectedElementView({ toggleParseHookNames, symbolicatedSourcePromise, }: Props): React.Node { - const {owners, rendererPackageName, rendererVersion, rootType, source} = - inspectedElement; + const { + owners, + rendererPackageName, + rendererVersion, + rootType, + source, + nativeTag, + } = inspectedElement; const bridge = useContext(BridgeContext); const store = useContext(StoreContext); @@ -75,6 +81,7 @@ export default function InspectedElementView({
diff --git a/packages/react-devtools-shared/src/devtools/views/Components/NativeTagBadge.css b/packages/react-devtools-shared/src/devtools/views/Components/NativeTagBadge.css new file mode 100644 index 0000000000..7188a53743 --- /dev/null +++ b/packages/react-devtools-shared/src/devtools/views/Components/NativeTagBadge.css @@ -0,0 +1,11 @@ +.Toggle { + display: flex; +} + +.Toggle > span { /* targets .ToggleContent */ + padding: 0; +} + +.Badge { + cursor: help; +} diff --git a/packages/react-devtools-shared/src/devtools/views/Components/NativeTagBadge.js b/packages/react-devtools-shared/src/devtools/views/Components/NativeTagBadge.js new file mode 100644 index 0000000000..118255b536 --- /dev/null +++ b/packages/react-devtools-shared/src/devtools/views/Components/NativeTagBadge.js @@ -0,0 +1,31 @@ +/** + * 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 + */ + +import * as React from 'react'; + +import Badge from './Badge'; +import Toggle from '../Toggle'; + +import styles from './NativeTagBadge.css'; + +type Props = { + nativeTag: number, +}; + +const noop = () => {}; +const title = + 'Unique identifier for the corresponding native component. React Native only.'; + +export default function NativeTagBadge({nativeTag}: Props): React.Node { + return ( + + Tag {nativeTag} + + ); +} diff --git a/packages/react-devtools-shared/src/frontend/types.js b/packages/react-devtools-shared/src/frontend/types.js index 827ba4dccb..af4aac5503 100644 --- a/packages/react-devtools-shared/src/frontend/types.js +++ b/packages/react-devtools-shared/src/frontend/types.js @@ -259,6 +259,9 @@ export type InspectedElement = { // UI plugins/visualizations for the inspected element. plugins: Plugins, + + // React Native only. + nativeTag: number | null, }; // TODO: Add profiling type