Skip to content

Commit

Permalink
React Native: Export getInspectorDataForInstance API (#21572)
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
Brian Vaughn committed Jul 26, 2021
1 parent c76e4db commit 4758e45
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 63 deletions.
4 changes: 4 additions & 0 deletions packages/react-native-renderer/src/ReactFabric.js
Expand Up @@ -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';
Expand Down Expand Up @@ -260,6 +261,9 @@ export {
unmountComponentAtNode,
stopSurface,
createPortal,
// This export is typically undefined in production builds.
// See the "enableGetInspectorDataForInstanceInProduction" flag.
getInspectorDataForInstance,
};

injectIntoDevTools({
Expand Down
126 changes: 71 additions & 55 deletions packages/react-native-renderer/src/ReactNativeFiberInspector.js
Expand Up @@ -19,67 +19,24 @@ 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 = {};
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 => {
Expand Down Expand Up @@ -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 {
Expand All @@ -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);

Expand Down Expand Up @@ -258,4 +270,8 @@ if (__DEV__) {
};
}

export {getInspectorDataForViewAtPoint, getInspectorDataForViewTag};
export {
getInspectorDataForInstance,
getInspectorDataForViewAtPoint,
getInspectorDataForViewTag,
};
4 changes: 4 additions & 0 deletions packages/react-native-renderer/src/ReactNativeRenderer.js
Expand Up @@ -36,6 +36,7 @@ import {
UIManager,
legacySendAccessibilityEvent,
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
import {getInspectorDataForInstance} from './ReactNativeFiberInspector';

import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
import {
Expand Down Expand Up @@ -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({
Expand Down
3 changes: 3 additions & 0 deletions packages/shared/ReactFeatureFlags.js
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/forks/ReactFeatureFlags.native-fb.js
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion packages/shared/forks/ReactFeatureFlags.native-oss.js
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion packages/shared/forks/ReactFeatureFlags.test-renderer.js
Expand Up @@ -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;

Expand Down
Expand Up @@ -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;

Expand Down
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion packages/shared/forks/ReactFeatureFlags.testing.js
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion packages/shared/forks/ReactFeatureFlags.testing.www.js
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion packages/shared/forks/ReactFeatureFlags.www.js
Expand Up @@ -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;

Expand Down

0 comments on commit 4758e45

Please sign in to comment.