From 4758e4533e933d29b1c3a960794eef354800f786 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 26 Jul 2021 09:56:59 -0400 Subject: [PATCH] React Native: Export getInspectorDataForInstance API (#21572) This PR exports a new top-level API, getInspectorDataForInstance, for React Native (both development and production). Although this change adds a new export to the DEV bundle, it only impacts the production bundle for internal builds (not what's published to NPM). --- .../react-native-renderer/src/ReactFabric.js | 4 + .../src/ReactNativeFiberInspector.js | 126 ++++++++++-------- .../src/ReactNativeRenderer.js | 4 + packages/shared/ReactFeatureFlags.js | 3 + .../forks/ReactFeatureFlags.native-fb.js | 2 +- .../forks/ReactFeatureFlags.native-oss.js | 2 +- .../forks/ReactFeatureFlags.test-renderer.js | 2 +- .../ReactFeatureFlags.test-renderer.native.js | 2 +- .../ReactFeatureFlags.test-renderer.www.js | 2 +- .../shared/forks/ReactFeatureFlags.testing.js | 2 +- .../forks/ReactFeatureFlags.testing.www.js | 2 +- .../shared/forks/ReactFeatureFlags.www.js | 2 +- 12 files changed, 90 insertions(+), 63 deletions(-) diff --git a/packages/react-native-renderer/src/ReactFabric.js b/packages/react-native-renderer/src/ReactFabric.js index 6fe3eeb12d244..744c1c178397f 100644 --- a/packages/react-native-renderer/src/ReactFabric.js +++ b/packages/react-native-renderer/src/ReactFabric.js @@ -23,6 +23,7 @@ import { injectIntoDevTools, getPublicRootInstance, } from 'react-reconciler/src/ReactFiberReconciler'; +import {getInspectorDataForInstance} from './ReactNativeFiberInspector'; import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal'; import {setBatchingImplementation} from './legacy-events/ReactGenericBatching'; @@ -260,6 +261,9 @@ export { unmountComponentAtNode, stopSurface, createPortal, + // This export is typically undefined in production builds. + // See the "enableGetInspectorDataForInstanceInProduction" flag. + getInspectorDataForInstance, }; injectIntoDevTools({ diff --git a/packages/react-native-renderer/src/ReactNativeFiberInspector.js b/packages/react-native-renderer/src/ReactNativeFiberInspector.js index ba8474fef23e5..9579384ef5e97 100644 --- a/packages/react-native-renderer/src/ReactNativeFiberInspector.js +++ b/packages/react-native-renderer/src/ReactNativeFiberInspector.js @@ -19,7 +19,7 @@ import {HostComponent} from 'react-reconciler/src/ReactWorkTags'; import invariant from 'shared/invariant'; // Module provided by RN: import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; - +import {enableGetInspectorDataForInstanceInProduction} from 'shared/ReactFeatureFlags'; import {getClosestInstanceFromNode} from './ReactNativeComponentTree'; const emptyObject = {}; @@ -27,59 +27,16 @@ if (__DEV__) { Object.freeze(emptyObject); } -let getInspectorDataForViewTag; -let getInspectorDataForViewAtPoint; - -if (__DEV__) { - const traverseOwnerTreeUp = function(hierarchy, instance: any) { - if (instance) { - hierarchy.unshift(instance); - traverseOwnerTreeUp(hierarchy, instance._debugOwner); - } - }; - - const getOwnerHierarchy = function(instance: any) { - const hierarchy = []; - traverseOwnerTreeUp(hierarchy, instance); - return hierarchy; - }; - - const lastNonHostInstance = function(hierarchy) { - for (let i = hierarchy.length - 1; i > 1; i--) { - const instance = hierarchy[i]; - - if (instance.tag !== HostComponent) { - return instance; - } - } - return hierarchy[0]; - }; - - const getHostProps = function(fiber) { - const host = findCurrentHostFiber(fiber); - if (host) { - return host.memoizedProps || emptyObject; - } - return emptyObject; - }; - - const getHostNode = function(fiber: Fiber | null, findNodeHandle) { - let hostNode; - // look for children first for the hostNode - // as composite fibers do not have a hostNode - while (fiber) { - if (fiber.stateNode !== null && fiber.tag === HostComponent) { - hostNode = findNodeHandle(fiber.stateNode); - } - if (hostNode) { - return hostNode; - } - fiber = fiber.child; - } - return null; - }; +let createHierarchy; +let getHostNode; +let getHostProps; +let lastNonHostInstance; +let getInspectorDataForInstance; +let getOwnerHierarchy; +let traverseOwnerTreeUp; - const createHierarchy = function(fiberHierarchy) { +if (__DEV__ || enableGetInspectorDataForInstanceInProduction) { + createHierarchy = function(fiberHierarchy) { return fiberHierarchy.map(fiber => ({ name: getComponentNameFromType(fiber.type), getInspectorData: findNodeHandle => { @@ -108,7 +65,33 @@ if (__DEV__) { })); }; - const getInspectorDataForInstance = function(closestInstance): InspectorData { + getHostNode = function(fiber: Fiber | null, findNodeHandle) { + let hostNode; + // look for children first for the hostNode + // as composite fibers do not have a hostNode + while (fiber) { + if (fiber.stateNode !== null && fiber.tag === HostComponent) { + hostNode = findNodeHandle(fiber.stateNode); + } + if (hostNode) { + return hostNode; + } + fiber = fiber.child; + } + return null; + }; + + getHostProps = function(fiber) { + const host = findCurrentHostFiber(fiber); + if (host) { + return host.memoizedProps || emptyObject; + } + return emptyObject; + }; + + getInspectorDataForInstance = function( + closestInstance: Fiber | null, + ): InspectorData { // Handle case where user clicks outside of ReactNative if (!closestInstance) { return { @@ -135,6 +118,35 @@ if (__DEV__) { }; }; + getOwnerHierarchy = function(instance: any) { + const hierarchy = []; + traverseOwnerTreeUp(hierarchy, instance); + return hierarchy; + }; + + lastNonHostInstance = function(hierarchy) { + for (let i = hierarchy.length - 1; i > 1; i--) { + const instance = hierarchy[i]; + + if (instance.tag !== HostComponent) { + return instance; + } + } + return hierarchy[0]; + }; + + traverseOwnerTreeUp = function(hierarchy, instance: any) { + if (instance) { + hierarchy.unshift(instance); + traverseOwnerTreeUp(hierarchy, instance._debugOwner); + } + }; +} + +let getInspectorDataForViewTag; +let getInspectorDataForViewAtPoint; + +if (__DEV__) { getInspectorDataForViewTag = function(viewTag: number): Object { const closestInstance = getClosestInstanceFromNode(viewTag); @@ -258,4 +270,8 @@ if (__DEV__) { }; } -export {getInspectorDataForViewAtPoint, getInspectorDataForViewTag}; +export { + getInspectorDataForInstance, + getInspectorDataForViewAtPoint, + getInspectorDataForViewTag, +}; diff --git a/packages/react-native-renderer/src/ReactNativeRenderer.js b/packages/react-native-renderer/src/ReactNativeRenderer.js index 3c3f234e0cb66..a60683b47b2ee 100644 --- a/packages/react-native-renderer/src/ReactNativeRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeRenderer.js @@ -36,6 +36,7 @@ import { UIManager, legacySendAccessibilityEvent, } from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; +import {getInspectorDataForInstance} from './ReactNativeFiberInspector'; import {getClosestInstanceFromNode} from './ReactNativeComponentTree'; import { @@ -264,6 +265,9 @@ export { createPortal, batchedUpdates as unstable_batchedUpdates, Internals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + // This export is typically undefined in production builds. + // See the "enableGetInspectorDataForInstanceInProduction" flag. + getInspectorDataForInstance, }; injectIntoDevTools({ diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 862a6bde9b264..a1689f64a5d8e 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -105,6 +105,9 @@ export const enableNewReconciler = false; export const disableNativeComponentFrames = false; +// Internal only. +export const enableGetInspectorDataForInstanceInProduction = false; + // Errors that are thrown while unmounting (or after in the case of passive effects) // should bypass any error boundaries that are also unmounting (or have unmounted) // and be handled by the nearest still-mounted boundary. diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 76cfa7d18f3ea..9e52536fbb36d 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -47,7 +47,7 @@ export const disableNativeComponentFrames = false; export const skipUnmountedBoundaries = false; export const deletedTreeCleanUpLevel = 3; export const enableSuspenseLayoutEffectSemantics = false; - +export const enableGetInspectorDataForInstanceInProduction = true; export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index f8595c01b9274..b34b583f18861 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false; export const skipUnmountedBoundaries = false; export const deletedTreeCleanUpLevel = 3; export const enableSuspenseLayoutEffectSemantics = false; - +export const enableGetInspectorDataForInstanceInProduction = false; export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 528f42ddc3cc0..7357217b16912 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false; export const skipUnmountedBoundaries = false; export const deletedTreeCleanUpLevel = 3; export const enableSuspenseLayoutEffectSemantics = false; - +export const enableGetInspectorDataForInstanceInProduction = false; export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index 6a70e11a4c557..10a05e52e152a 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false; export const skipUnmountedBoundaries = false; export const deletedTreeCleanUpLevel = 3; export const enableSuspenseLayoutEffectSemantics = false; - +export const enableGetInspectorDataForInstanceInProduction = false; export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index ef095eef7e7bd..617ec8f0bc186 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false; export const skipUnmountedBoundaries = false; export const deletedTreeCleanUpLevel = 3; export const enableSuspenseLayoutEffectSemantics = false; - +export const enableGetInspectorDataForInstanceInProduction = false; export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index 58c6400b0aeef..50e1c73f8dd20 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false; export const skipUnmountedBoundaries = false; export const deletedTreeCleanUpLevel = 3; export const enableSuspenseLayoutEffectSemantics = false; - +export const enableGetInspectorDataForInstanceInProduction = false; export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index 0c3d15890b277..d16f6212c5485 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false; export const skipUnmountedBoundaries = true; export const deletedTreeCleanUpLevel = 3; export const enableSuspenseLayoutEffectSemantics = false; - +export const enableGetInspectorDataForInstanceInProduction = false; export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index e028c9ddb8ef9..dadf7972612f5 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -60,7 +60,7 @@ export const warnAboutDeprecatedLifecycles = true; export const disableLegacyContext = __EXPERIMENTAL__; export const warnAboutStringRefs = false; export const warnAboutDefaultPropsOnFunctionComponents = false; - +export const enableGetInspectorDataForInstanceInProduction = false; export const enableSuspenseServerRenderer = true; export const enableSelectiveHydration = true;