@samitouri / QOS-React-2 / commits / 32480a0462

Add component stack to invalid-object-child error (#9415)

https://twitter.com/EsaMatti/status/835952325525188608 (28 likes!)

Ben Alpert committed Apr 20, 2017 at 09:49 UTC 32480a0462e94be510095c668e92061c02107597
5 files changed +55 -65
src/isomorphic/children/traverseAllChildren.js
+9 -18
@@ -12,13 +12,18 @@
12 'use strict';
13
14 var REACT_ELEMENT_TYPE = require('ReactElementSymbol');
15 -var ReactCurrentOwner = require('ReactCurrentOwner');
15
16 var getIteratorFn = require('getIteratorFn');
17 var invariant = require('fbjs/lib/invariant');
18 var KeyEscapeUtils = require('KeyEscapeUtils');
19 var warning = require('fbjs/lib/warning');
20
21 +if (__DEV__) {
22 + var {
23 + getCurrentStackAddendum,
24 + } = require('ReactComponentTreeHook');
25 +}
26 +
27 var SEPARATOR = '.';
28 var SUBSEPARATOR = ':';
29
@@ -114,21 +119,12 @@ function traverseAllChildrenImpl(
119 if (__DEV__) {
120 // Warn about using Maps as children
121 if (iteratorFn === children.entries) {
117 - let mapsAsChildrenAddendum = '';
118 - if (ReactCurrentOwner.current) {
119 - var mapsAsChildrenOwnerName = ReactCurrentOwner.current.getName();
120 - if (mapsAsChildrenOwnerName) {
121 - mapsAsChildrenAddendum = '\n\nCheck the render method of `' +
122 - mapsAsChildrenOwnerName +
123 - '`.';
124 - }
125 - }
122 warning(
123 didWarnAboutMaps,
124 'Using Maps as children is unsupported and will likely yield ' +
125 'unexpected results. Convert it to a sequence/iterable of keyed ' +
126 'ReactElements instead.%s',
131 - mapsAsChildrenAddendum,
127 + getCurrentStackAddendum(),
128 );
129 didWarnAboutMaps = true;
130 }
@@ -151,13 +147,8 @@ function traverseAllChildrenImpl(
147 var addendum = '';
148 if (__DEV__) {
149 addendum = ' If you meant to render a collection of children, use an array ' +
154 - 'instead.';
155 - if (ReactCurrentOwner.current) {
156 - var name = ReactCurrentOwner.current.getName();
157 - if (name) {
158 - addendum += '\n\nCheck the render method of `' + name + '`.';
159 - }
160 - }
150 + 'instead.' +
151 + getCurrentStackAddendum();
152 }
153 var childrenString = '' + children;
154 invariant(
src/renderers/__tests__/ReactComponent-test.js
+29 -6
@@ -17,6 +17,10 @@ var ReactDOMFeatureFlags;
17 var ReactTestUtils;
18
19 describe('ReactComponent', () => {
20 + function normalizeCodeLocInfo(str) {
21 + return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
22 + }
23 +
24 beforeEach(() => {
25 React = require('react');
26 ReactDOM = require('react-dom');
@@ -389,10 +393,19 @@ describe('ReactComponent', () => {
393 };
394 var element = <div>{[children]}</div>;
395 var container = document.createElement('div');
392 - expect(() => ReactDOM.render(element, container)).toThrowError(
396 + var ex;
397 + try {
398 + ReactDOM.render(element, container);
399 + } catch (e) {
400 + ex = e;
401 + }
402 + expect(ex).toBeDefined();
403 + expect(normalizeCodeLocInfo(ex.message)).toBe(
404 'Objects are not valid as a React child (found: object with keys ' +
394 - '{x, y, z}). If you meant to render a collection of children, use an ' +
395 - 'array instead.',
405 + '{x, y, z}). If you meant to render a collection of children, use ' +
406 + 'an array instead.' +
407 + // Fiber gives a slightly better stack with the nearest host components
408 + (ReactDOMFeatureFlags.useFiber ? '\n in div (at **)' : ''),
409 );
410 });
411
@@ -408,10 +421,20 @@ describe('ReactComponent', () => {
421 }
422 }
423 var container = document.createElement('div');
411 - expect(() => ReactDOM.render(<Foo />, container)).toThrowError(
424 + var ex;
425 + try {
426 + ReactDOM.render(<Foo />, container);
427 + } catch (e) {
428 + ex = e;
429 + }
430 + expect(ex).toBeDefined();
431 + expect(normalizeCodeLocInfo(ex.message)).toBe(
432 'Objects are not valid as a React child (found: object with keys ' +
413 - '{a, b, c}). If you meant to render a collection of children, use an ' +
414 - 'array instead.\n\nCheck the render method of `Foo`.',
433 + '{a, b, c}). If you meant to render a collection of children, use ' +
434 + 'an array instead.\n' +
435 + // Fiber gives a slightly better stack with the nearest host components
436 + (ReactDOMFeatureFlags.useFiber ? ' in div (at **)\n' : '') +
437 + ' in Foo (at **)',
438 );
439 });
440 });
src/renderers/__tests__/ReactMultiChild-test.js
+5 -2
@@ -274,10 +274,13 @@ describe('ReactMultiChild', () => {
274 var container = document.createElement('div');
275 ReactDOM.render(<Parent />, container);
276 expectDev(console.error.calls.count()).toBe(1);
277 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
277 + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
278 'Warning: Using Maps as children is unsupported and will likely yield ' +
279 'unexpected results. Convert it to a sequence/iterable of keyed ' +
280 - 'ReactElements instead.\n\nCheck the render method of `Parent`.',
280 + 'ReactElements instead.\n' +
281 + // Fiber gives a slightly better stack with the nearest host components
282 + (ReactDOMFeatureFlags.useFiber ? ' in div (at **)\n' : '') +
283 + ' in Parent (at **)',
284 );
285 });
286
src/renderers/shared/fiber/ReactChildFiber.js
+3 -21
@@ -36,11 +36,9 @@ var emptyObject = require('fbjs/lib/emptyObject');
36 var getIteratorFn = require('getIteratorFn');
37 var invariant = require('fbjs/lib/invariant');
38 var ReactFeatureFlags = require('ReactFeatureFlags');
39 -var {ReactCurrentOwner} = require('ReactGlobalSharedState');
39
40 if (__DEV__) {
41 var {getCurrentFiberStackAddendum} = require('ReactDebugCurrentFiber');
43 - var getComponentName = require('getComponentName');
42 var warning = require('fbjs/lib/warning');
43 var didWarnAboutMaps = false;
44 /**
@@ -164,14 +162,8 @@ function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) {
162 let addendum = '';
163 if (__DEV__) {
164 addendum = ' If you meant to render a collection of children, use an array ' +
167 - 'instead.';
168 - const owner = ReactCurrentOwner.owner || returnFiber._debugOwner;
169 - if (owner && typeof owner.tag === 'number') {
170 - const name = getComponentName((owner: any));
171 - if (name) {
172 - addendum += '\n\nCheck the render method of `' + name + '`.';
173 - }
174 - }
165 + 'instead.' +
166 + (getCurrentFiberStackAddendum() || '');
167 }
168 invariant(
169 false,
@@ -854,22 +846,12 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
846 if (typeof newChildrenIterable.entries === 'function') {
847 const possibleMap = (newChildrenIterable: any);
848 if (possibleMap.entries === iteratorFn) {
857 - let mapsAsChildrenAddendum = '';
858 - const owner = ReactCurrentOwner.owner || returnFiber._debugOwner;
859 - if (owner && typeof owner.tag === 'number') {
860 - const mapsAsChildrenOwnerName = getComponentName((owner: any));
861 - if (mapsAsChildrenOwnerName) {
862 - mapsAsChildrenAddendum = '\n\nCheck the render method of `' +
863 - mapsAsChildrenOwnerName +
864 - '`.';
865 - }
866 - }
849 warning(
850 didWarnAboutMaps,
851 'Using Maps as children is unsupported and will likely yield ' +
852 'unexpected results. Convert it to a sequence/iterable of keyed ' +
853 'ReactElements instead.%s',
872 - mapsAsChildrenAddendum,
854 + getCurrentFiberStackAddendum(),
855 );
856 didWarnAboutMaps = true;
857 }
src/renderers/shared/stack/reconciler/traverseStackChildren.js
+9 -18
@@ -12,13 +12,18 @@
12 'use strict';
13
14 var REACT_ELEMENT_TYPE = require('ReactElementSymbol');
15 -var {ReactCurrentOwner} = require('ReactGlobalSharedState');
15
16 var getIteratorFn = require('getIteratorFn');
17 var invariant = require('fbjs/lib/invariant');
18 var KeyEscapeUtils = require('KeyEscapeUtils');
19 var warning = require('fbjs/lib/warning');
20
21 +if (__DEV__) {
22 + var {
23 + getCurrentStackAddendum,
24 + } = require('ReactGlobalSharedState').ReactComponentTreeHook;
25 +}
26 +
27 var SEPARATOR = '.';
28 var SUBSEPARATOR = ':';
29
@@ -114,21 +119,12 @@ function traverseStackChildrenImpl(
119 if (__DEV__) {
120 // Warn about using Maps as children
121 if (iteratorFn === children.entries) {
117 - let mapsAsChildrenAddendum = '';
118 - if (ReactCurrentOwner.current) {
119 - var mapsAsChildrenOwnerName = ReactCurrentOwner.current.getName();
120 - if (mapsAsChildrenOwnerName) {
121 - mapsAsChildrenAddendum = '\n\nCheck the render method of `' +
122 - mapsAsChildrenOwnerName +
123 - '`.';
124 - }
125 - }
122 warning(
123 didWarnAboutMaps,
124 'Using Maps as children is unsupported and will likely yield ' +
125 'unexpected results. Convert it to a sequence/iterable of keyed ' +
126 'ReactElements instead.%s',
131 - mapsAsChildrenAddendum,
127 + getCurrentStackAddendum(),
128 );
129 didWarnAboutMaps = true;
130 }
@@ -151,13 +147,8 @@ function traverseStackChildrenImpl(
147 var addendum = '';
148 if (__DEV__) {
149 addendum = ' If you meant to render a collection of children, use an array ' +
154 - 'instead.';
155 - if (ReactCurrentOwner.current) {
156 - var name = ReactCurrentOwner.current.getName();
157 - if (name) {
158 - addendum += '\n\nCheck the render method of `' + name + '`.';
159 - }
160 - }
150 + 'instead.' +
151 + getCurrentStackAddendum();
152 }
153 var childrenString = '' + children;
154 invariant(