Skip to content

Commit efa7148

Browse files
authored
Warn about unresolved function as a child (facebook#10376)
* Warn about unresolved function as a child * Oops
1 parent 6f28ecf commit efa7148

File tree

2 files changed

+112
-2
lines changed

2 files changed

+112
-2
lines changed

src/renderers/__tests__/ReactComponent-test.js

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
var React;
1515
var ReactDOM;
1616
var ReactDOMServer;
17-
var ReactDOMFeatureFlags;
1817
var ReactTestUtils;
1918

19+
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
20+
2021
describe('ReactComponent', () => {
2122
function normalizeCodeLocInfo(str) {
2223
return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
@@ -26,7 +27,6 @@ describe('ReactComponent', () => {
2627
React = require('react');
2728
ReactDOM = require('react-dom');
2829
ReactDOMServer = require('react-dom/server');
29-
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
3030
ReactTestUtils = require('react-dom/test-utils');
3131
});
3232

@@ -493,4 +493,78 @@ describe('ReactComponent', () => {
493493
' in Foo (at **)',
494494
);
495495
});
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+
}
496570
});

src/renderers/shared/fiber/ReactChildFiber.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,16 @@ function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) {
200200
}
201201
}
202202

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+
203213
// This wrapper function exists because I expect to clone the code in each path
204214
// to be able to optimize each path individually by branching early. This needs
205215
// a compiler or we can do it manually. Helpers that don't need this branching
@@ -565,6 +575,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
565575
throwOnInvalidObjectType(returnFiber, newChild);
566576
}
567577

578+
if (__DEV__) {
579+
const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
580+
if (!disableNewFiberFeatures && typeof newChild === 'function') {
581+
warnOnFunctionType();
582+
}
583+
}
584+
568585
return null;
569586
}
570587

@@ -638,6 +655,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
638655
throwOnInvalidObjectType(returnFiber, newChild);
639656
}
640657

658+
if (__DEV__) {
659+
const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
660+
if (!disableNewFiberFeatures && typeof newChild === 'function') {
661+
warnOnFunctionType();
662+
}
663+
}
664+
641665
return null;
642666
}
643667

@@ -697,6 +721,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
697721
throwOnInvalidObjectType(returnFiber, newChild);
698722
}
699723

724+
if (__DEV__) {
725+
const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
726+
if (!disableNewFiberFeatures && typeof newChild === 'function') {
727+
warnOnFunctionType();
728+
}
729+
}
730+
700731
return null;
701732
}
702733

@@ -1418,6 +1449,11 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
14181449
throwOnInvalidObjectType(returnFiber, newChild);
14191450
}
14201451

1452+
if (__DEV__) {
1453+
if (!disableNewFiberFeatures && typeof newChild === 'function') {
1454+
warnOnFunctionType();
1455+
}
1456+
}
14211457
if (!disableNewFiberFeatures && typeof newChild === 'undefined') {
14221458
// If the new child is undefined, and the return fiber is a composite
14231459
// component, throw an error. If Fiber return types are disabled,

0 commit comments

Comments
 (0)