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(