@samitouri / QOS-React / commits / 7a3416f275

Expose component stack from reactTag to React Native renderer (#12549)

This is not safe in general and therefore shouldn't be exposed to anything other than React Native internals. It will also need a different version in Fabric that will not have the reactTag exposed.

Sebastian Markbåge committed Apr 4, 2018 at 17:18 UTC 7a3416f27532ac25849dfbc505300d469b43bbcc
2 files changed +92
packages/react-native-renderer/src/ReactNativeRenderer.js
+11
@@ -19,6 +19,8 @@ import ReactVersion from 'shared/ReactVersion';
19 // Module provided by RN:
20 import UIManager from 'UIManager';
21
22 +import {getStackAddendumByWorkInProgressFiber} from 'shared/ReactFiberComponentTreeHook';
23 +
24 import NativeMethodsMixin from './NativeMethodsMixin';
25 import ReactNativeBridgeEventPlugin from './ReactNativeBridgeEventPlugin';
26 import ReactNativeComponent from './ReactNativeComponent';
@@ -35,6 +37,14 @@ injectFindHostInstance(ReactNativeFiberRenderer.findHostInstance);
37
38 ReactGenericBatching.injection.injectRenderer(ReactNativeFiberRenderer);
39
40 +function computeComponentStackForErrorReporting(reactTag: number): string {
41 + let fiber = ReactNativeComponentTree.getClosestInstanceFromNode(reactTag);
42 + if (!fiber) {
43 + return '';
44 + }
45 + return getStackAddendumByWorkInProgressFiber(fiber);
46 +}
47 +
48 const roots = new Map();
49
50 const ReactNativeRenderer: ReactNativeType = {
@@ -99,6 +109,7 @@ const ReactNativeRenderer: ReactNativeType = {
109 TouchHistoryMath, // PanResponder
110 createReactNativeComponentClass, // RCTText, RCTView, ReactNativeART
111 takeSnapshot, // react-native-implementation
112 + computeComponentStackForErrorReporting,
113 },
114 };
115
packages/react-native-renderer/src/__tests__/ReactNativeError-test.internal.js new
+81
@@ -0,0 +1,81 @@
1 +/**
2 + * Copyright (c) 2013-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + * @jest-environment node
9 + */
10 +
11 +'use strict';
12 +
13 +let React;
14 +let ReactNative;
15 +let createReactNativeComponentClass;
16 +let computeComponentStackForErrorReporting;
17 +
18 +function normalizeCodeLocInfo(str) {
19 + return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
20 +}
21 +
22 +describe('ReactNativeError', () => {
23 + beforeEach(() => {
24 + jest.resetModules();
25 +
26 + React = require('react');
27 + ReactNative = require('react-native-renderer');
28 + createReactNativeComponentClass = require('../createReactNativeComponentClass')
29 + .default;
30 + computeComponentStackForErrorReporting =
31 + ReactNative.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
32 + .computeComponentStackForErrorReporting;
33 + });
34 +
35 + it('should be able to extract a component stack from a native view', () => {
36 + const View = createReactNativeComponentClass('View', () => ({
37 + validAttributes: {foo: true},
38 + uiViewClassName: 'View',
39 + }));
40 +
41 + const ref = React.createRef();
42 +
43 + function FunctionalComponent(props) {
44 + return props.children;
45 + }
46 +
47 + class ClassComponent extends React.Component {
48 + render() {
49 + return (
50 + <FunctionalComponent>
51 + <View foo="test" ref={ref} />
52 + </FunctionalComponent>
53 + );
54 + }
55 + }
56 +
57 + ReactNative.render(<ClassComponent />, 1);
58 +
59 + let reactTag = ReactNative.findNodeHandle(ref.current);
60 +
61 + let componentStack = normalizeCodeLocInfo(
62 + computeComponentStackForErrorReporting(reactTag),
63 + );
64 +
65 + if (__DEV__) {
66 + expect(componentStack).toBe(
67 + '\n' +
68 + ' in View (at **)\n' +
69 + ' in FunctionalComponent (at **)\n' +
70 + ' in ClassComponent (at **)',
71 + );
72 + } else {
73 + expect(componentStack).toBe(
74 + '\n' +
75 + ' in View\n' +
76 + ' in FunctionalComponent\n' +
77 + ' in ClassComponent',
78 + );
79 + }
80 + });
81 +});