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).
Brian Vaughn committed
Jul 26, 2021 at 09:56 UTC
4758e4533e933d29b1c3a960794eef354800f786
12 files changed
+90
-63
packages/react-native-renderer/src/ReactFabric.js
+4
@@ -23,6 +23,7 @@ import {
23
injectIntoDevTools,
24
getPublicRootInstance,
25
} from 'react-reconciler/src/ReactFiberReconciler';
26
+import {getInspectorDataForInstance} from './ReactNativeFiberInspector';
27
28
import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
29
import {setBatchingImplementation} from './legacy-events/ReactGenericBatching';
@@ -260,6 +261,9 @@ export {
261
unmountComponentAtNode,
262
stopSurface,
263
createPortal,
264
+ // This export is typically undefined in production builds.
265
+ // See the "enableGetInspectorDataForInstanceInProduction" flag.
266
+ getInspectorDataForInstance,
267
};
268
269
injectIntoDevTools({
packages/react-native-renderer/src/ReactNativeFiberInspector.js
+71
-55
@@ -19,7 +19,7 @@ import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
19
import invariant from 'shared/invariant';
20
// Module provided by RN:
21
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
22
-
22
+import {enableGetInspectorDataForInstanceInProduction} from 'shared/ReactFeatureFlags';
23
import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
24
25
const emptyObject = {};
@@ -27,59 +27,16 @@ if (__DEV__) {
27
Object.freeze(emptyObject);
28
}
29
30
-let getInspectorDataForViewTag;
31
-let getInspectorDataForViewAtPoint;
32
-
33
-if (__DEV__) {
34
- const traverseOwnerTreeUp = function(hierarchy, instance: any) {
35
- if (instance) {
36
- hierarchy.unshift(instance);
37
- traverseOwnerTreeUp(hierarchy, instance._debugOwner);
38
- }
39
- };
40
-
41
- const getOwnerHierarchy = function(instance: any) {
42
- const hierarchy = [];
43
- traverseOwnerTreeUp(hierarchy, instance);
44
- return hierarchy;
45
- };
46
-
47
- const lastNonHostInstance = function(hierarchy) {
48
- for (let i = hierarchy.length - 1; i > 1; i--) {
49
- const instance = hierarchy[i];
50
-
51
- if (instance.tag !== HostComponent) {
52
- return instance;
53
- }
54
- }
55
- return hierarchy[0];
56
- };
57
-
58
- const getHostProps = function(fiber) {
59
- const host = findCurrentHostFiber(fiber);
60
- if (host) {
61
- return host.memoizedProps || emptyObject;
62
- }
63
- return emptyObject;
64
- };
65
-
66
- const getHostNode = function(fiber: Fiber | null, findNodeHandle) {
67
- let hostNode;
68
- // look for children first for the hostNode
69
- // as composite fibers do not have a hostNode
70
- while (fiber) {
71
- if (fiber.stateNode !== null && fiber.tag === HostComponent) {
72
- hostNode = findNodeHandle(fiber.stateNode);
73
- }
74
- if (hostNode) {
75
- return hostNode;
76
- }
77
- fiber = fiber.child;
78
- }
79
- return null;
80
- };
30
+let createHierarchy;
31
+let getHostNode;
32
+let getHostProps;
33
+let lastNonHostInstance;
34
+let getInspectorDataForInstance;
35
+let getOwnerHierarchy;
36
+let traverseOwnerTreeUp;
37
82
- const createHierarchy = function(fiberHierarchy) {
38
+if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
39
+ createHierarchy = function(fiberHierarchy) {
40
return fiberHierarchy.map(fiber => ({
41
name: getComponentNameFromType(fiber.type),
42
getInspectorData: findNodeHandle => {
@@ -108,7 +65,33 @@ if (__DEV__) {
65
}));
66
};
67
111
- const getInspectorDataForInstance = function(closestInstance): InspectorData {
68
+ getHostNode = function(fiber: Fiber | null, findNodeHandle) {
69
+ let hostNode;
70
+ // look for children first for the hostNode
71
+ // as composite fibers do not have a hostNode
72
+ while (fiber) {
73
+ if (fiber.stateNode !== null && fiber.tag === HostComponent) {
74
+ hostNode = findNodeHandle(fiber.stateNode);
75
+ }
76
+ if (hostNode) {
77
+ return hostNode;
78
+ }
79
+ fiber = fiber.child;
80
+ }
81
+ return null;
82
+ };
83
+
84
+ getHostProps = function(fiber) {
85
+ const host = findCurrentHostFiber(fiber);
86
+ if (host) {
87
+ return host.memoizedProps || emptyObject;
88
+ }
89
+ return emptyObject;
90
+ };
91
+
92
+ getInspectorDataForInstance = function(
93
+ closestInstance: Fiber | null,
94
+ ): InspectorData {
95
// Handle case where user clicks outside of ReactNative
96
if (!closestInstance) {
97
return {
@@ -135,6 +118,35 @@ if (__DEV__) {
118
};
119
};
120
121
+ getOwnerHierarchy = function(instance: any) {
122
+ const hierarchy = [];
123
+ traverseOwnerTreeUp(hierarchy, instance);
124
+ return hierarchy;
125
+ };
126
+
127
+ lastNonHostInstance = function(hierarchy) {
128
+ for (let i = hierarchy.length - 1; i > 1; i--) {
129
+ const instance = hierarchy[i];
130
+
131
+ if (instance.tag !== HostComponent) {
132
+ return instance;
133
+ }
134
+ }
135
+ return hierarchy[0];
136
+ };
137
+
138
+ traverseOwnerTreeUp = function(hierarchy, instance: any) {
139
+ if (instance) {
140
+ hierarchy.unshift(instance);
141
+ traverseOwnerTreeUp(hierarchy, instance._debugOwner);
142
+ }
143
+ };
144
+}
145
+
146
+let getInspectorDataForViewTag;
147
+let getInspectorDataForViewAtPoint;
148
+
149
+if (__DEV__) {
150
getInspectorDataForViewTag = function(viewTag: number): Object {
151
const closestInstance = getClosestInstanceFromNode(viewTag);
152
@@ -258,4 +270,8 @@ if (__DEV__) {
270
};
271
}
272
261
-export {getInspectorDataForViewAtPoint, getInspectorDataForViewTag};
273
+export {
274
+ getInspectorDataForInstance,
275
+ getInspectorDataForViewAtPoint,
276
+ getInspectorDataForViewTag,
277
+};
packages/react-native-renderer/src/ReactNativeRenderer.js
+4
@@ -36,6 +36,7 @@ import {
36
UIManager,
37
legacySendAccessibilityEvent,
38
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
39
+import {getInspectorDataForInstance} from './ReactNativeFiberInspector';
40
41
import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
42
import {
@@ -264,6 +265,9 @@ export {
265
createPortal,
266
batchedUpdates as unstable_batchedUpdates,
267
Internals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
268
+ // This export is typically undefined in production builds.
269
+ // See the "enableGetInspectorDataForInstanceInProduction" flag.
270
+ getInspectorDataForInstance,
271
};
272
273
injectIntoDevTools({
packages/shared/ReactFeatureFlags.js
+3
@@ -105,6 +105,9 @@ export const enableNewReconciler = false;
105
106
export const disableNativeComponentFrames = false;
107
108
+// Internal only.
109
+export const enableGetInspectorDataForInstanceInProduction = false;
110
+
111
// Errors that are thrown while unmounting (or after in the case of passive effects)
112
// should bypass any error boundaries that are also unmounting (or have unmounted)
113
// and be handled by the nearest still-mounted boundary.
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
-1
@@ -47,7 +47,7 @@ export const disableNativeComponentFrames = false;
47
export const skipUnmountedBoundaries = false;
48
export const deletedTreeCleanUpLevel = 3;
49
export const enableSuspenseLayoutEffectSemantics = false;
50
-
50
+export const enableGetInspectorDataForInstanceInProduction = true;
51
export const enableNewReconciler = false;
52
export const deferRenderPhaseUpdateToNextBatch = false;
53
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
-1
@@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false;
46
export const skipUnmountedBoundaries = false;
47
export const deletedTreeCleanUpLevel = 3;
48
export const enableSuspenseLayoutEffectSemantics = false;
49
-
49
+export const enableGetInspectorDataForInstanceInProduction = false;
50
export const enableNewReconciler = false;
51
export const deferRenderPhaseUpdateToNextBatch = false;
52
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
-1
@@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false;
46
export const skipUnmountedBoundaries = false;
47
export const deletedTreeCleanUpLevel = 3;
48
export const enableSuspenseLayoutEffectSemantics = false;
49
-
49
+export const enableGetInspectorDataForInstanceInProduction = false;
50
export const enableNewReconciler = false;
51
export const deferRenderPhaseUpdateToNextBatch = false;
52
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
+1
-1
@@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false;
46
export const skipUnmountedBoundaries = false;
47
export const deletedTreeCleanUpLevel = 3;
48
export const enableSuspenseLayoutEffectSemantics = false;
49
-
49
+export const enableGetInspectorDataForInstanceInProduction = false;
50
export const enableNewReconciler = false;
51
export const deferRenderPhaseUpdateToNextBatch = false;
52
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
-1
@@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false;
46
export const skipUnmountedBoundaries = false;
47
export const deletedTreeCleanUpLevel = 3;
48
export const enableSuspenseLayoutEffectSemantics = false;
49
-
49
+export const enableGetInspectorDataForInstanceInProduction = false;
50
export const enableNewReconciler = false;
51
export const deferRenderPhaseUpdateToNextBatch = false;
52
packages/shared/forks/ReactFeatureFlags.testing.js
+1
-1
@@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false;
46
export const skipUnmountedBoundaries = false;
47
export const deletedTreeCleanUpLevel = 3;
48
export const enableSuspenseLayoutEffectSemantics = false;
49
-
49
+export const enableGetInspectorDataForInstanceInProduction = false;
50
export const enableNewReconciler = false;
51
export const deferRenderPhaseUpdateToNextBatch = false;
52
packages/shared/forks/ReactFeatureFlags.testing.www.js
+1
-1
@@ -46,7 +46,7 @@ export const disableNativeComponentFrames = false;
46
export const skipUnmountedBoundaries = true;
47
export const deletedTreeCleanUpLevel = 3;
48
export const enableSuspenseLayoutEffectSemantics = false;
49
-
49
+export const enableGetInspectorDataForInstanceInProduction = false;
50
export const enableNewReconciler = false;
51
export const deferRenderPhaseUpdateToNextBatch = false;
52
packages/shared/forks/ReactFeatureFlags.www.js
+1
-1
@@ -60,7 +60,7 @@ export const warnAboutDeprecatedLifecycles = true;
60
export const disableLegacyContext = __EXPERIMENTAL__;
61
export const warnAboutStringRefs = false;
62
export const warnAboutDefaultPropsOnFunctionComponents = false;
63
-
63
+export const enableGetInspectorDataForInstanceInProduction = false;
64
export const enableSuspenseServerRenderer = true;
65
export const enableSelectiveHydration = true;
66