Warn about unresolved function as a child (#10376)
* Warn about unresolved function as a child * Oops
Dan Abramov committed
Aug 4, 2017 at 15:51 UTC
efa71484b334153958d8a0f35310c3c4ee72c731
2 files changed
+112
-2
src/renderers/__tests__/ReactComponent-test.js
+76
-2
@@ -14,9 +14,10 @@
14
var React;
15
var ReactDOM;
16
var ReactDOMServer;
17
-var ReactDOMFeatureFlags;
17
var ReactTestUtils;
18
19
+var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
20
+
21
describe('ReactComponent', () => {
22
function normalizeCodeLocInfo(str) {
23
return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
@@ -26,7 +27,6 @@ describe('ReactComponent', () => {
27
React = require('react');
28
ReactDOM = require('react-dom');
29
ReactDOMServer = require('react-dom/server');
29
- ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
30
ReactTestUtils = require('react-dom/test-utils');
31
});
32
@@ -493,4 +493,78 @@ describe('ReactComponent', () => {
493
' in Foo (at **)',
494
);
495
});
496
+
497
+ if (ReactDOMFeatureFlags.useFiber) {
498
+ describe('with new features', () => {
499
+ beforeEach(() => {
500
+ require('ReactFeatureFlags').disableNewFiberFeatures = false;
501
+ });
502
+
503
+ it('warns on function as a return value from a function', () => {
504
+ function Foo() {
505
+ return Foo;
506
+ }
507
+ spyOn(console, 'error');
508
+ var container = document.createElement('div');
509
+ ReactDOM.render(<Foo />, container);
510
+ expectDev(console.error.calls.count()).toBe(1);
511
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
512
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
513
+ 'you return a Component instead of <Component /> from render. ' +
514
+ 'Or maybe you meant to call this function rather than return it.\n' +
515
+ ' in Foo (at **)',
516
+ );
517
+ });
518
+
519
+ it('warns on function as a return value from a class', () => {
520
+ class Foo extends React.Component {
521
+ render() {
522
+ return Foo;
523
+ }
524
+ }
525
+ spyOn(console, 'error');
526
+ var container = document.createElement('div');
527
+ ReactDOM.render(<Foo />, container);
528
+ expectDev(console.error.calls.count()).toBe(1);
529
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
530
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
531
+ 'you return a Component instead of <Component /> from render. ' +
532
+ 'Or maybe you meant to call this function rather than return it.\n' +
533
+ ' in Foo (at **)',
534
+ );
535
+ });
536
+
537
+ it('warns on function as a child to host component', () => {
538
+ function Foo() {
539
+ return <div><span>{Foo}</span></div>;
540
+ }
541
+ spyOn(console, 'error');
542
+ var container = document.createElement('div');
543
+ ReactDOM.render(<Foo />, container);
544
+ expectDev(console.error.calls.count()).toBe(1);
545
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
546
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
547
+ 'you return a Component instead of <Component /> from render. ' +
548
+ 'Or maybe you meant to call this function rather than return it.\n' +
549
+ ' in span (at **)\n' +
550
+ ' in div (at **)\n' +
551
+ ' in Foo (at **)',
552
+ );
553
+ });
554
+
555
+ it('does not warn for function-as-a-child that gets resolved', () => {
556
+ function Bar(props) {
557
+ return props.children();
558
+ }
559
+ function Foo() {
560
+ return <Bar>{() => 'Hello'}</Bar>;
561
+ }
562
+ spyOn(console, 'error');
563
+ var container = document.createElement('div');
564
+ ReactDOM.render(<Foo />, container);
565
+ expect(container.innerHTML).toBe('Hello');
566
+ expectDev(console.error.calls.count()).toBe(0);
567
+ });
568
+ });
569
+ }
570
});
src/renderers/shared/fiber/ReactChildFiber.js
+36
@@ -200,6 +200,16 @@ function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) {
200
}
201
}
202
203
+function warnOnFunctionType() {
204
+ warning(
205
+ false,
206
+ 'Functions are not valid as a React child. This may happen if ' +
207
+ 'you return a Component instead of <Component /> from render. ' +
208
+ 'Or maybe you meant to call this function rather than return it.%s',
209
+ getCurrentFiberStackAddendum() || '',
210
+ );
211
+}
212
+
213
// This wrapper function exists because I expect to clone the code in each path
214
// to be able to optimize each path individually by branching early. This needs
215
// a compiler or we can do it manually. Helpers that don't need this branching
@@ -565,6 +575,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
575
throwOnInvalidObjectType(returnFiber, newChild);
576
}
577
578
+ if (__DEV__) {
579
+ const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
580
+ if (!disableNewFiberFeatures && typeof newChild === 'function') {
581
+ warnOnFunctionType();
582
+ }
583
+ }
584
+
585
return null;
586
}
587
@@ -638,6 +655,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
655
throwOnInvalidObjectType(returnFiber, newChild);
656
}
657
658
+ if (__DEV__) {
659
+ const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
660
+ if (!disableNewFiberFeatures && typeof newChild === 'function') {
661
+ warnOnFunctionType();
662
+ }
663
+ }
664
+
665
return null;
666
}
667
@@ -697,6 +721,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
721
throwOnInvalidObjectType(returnFiber, newChild);
722
}
723
724
+ if (__DEV__) {
725
+ const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
726
+ if (!disableNewFiberFeatures && typeof newChild === 'function') {
727
+ warnOnFunctionType();
728
+ }
729
+ }
730
+
731
return null;
732
}
733
@@ -1418,6 +1449,11 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
1449
throwOnInvalidObjectType(returnFiber, newChild);
1450
}
1451
1452
+ if (__DEV__) {
1453
+ if (!disableNewFiberFeatures && typeof newChild === 'function') {
1454
+ warnOnFunctionType();
1455
+ }
1456
+ }
1457
if (!disableNewFiberFeatures && typeof newChild === 'undefined') {
1458
// If the new child is undefined, and the return fiber is a composite
1459
// component, throw an error. If Fiber return types are disabled,