diff --git a/src/renderers/__tests__/ReactComponent-test.js b/src/renderers/__tests__/ReactComponent-test.js index dbea2bd3dcd5f..a5f5ce287f643 100644 --- a/src/renderers/__tests__/ReactComponent-test.js +++ b/src/renderers/__tests__/ReactComponent-test.js @@ -14,9 +14,10 @@ var React; var ReactDOM; var ReactDOMServer; -var ReactDOMFeatureFlags; var ReactTestUtils; +var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); + describe('ReactComponent', () => { function normalizeCodeLocInfo(str) { return str && str.replace(/\(at .+?:\d+\)/g, '(at **)'); @@ -26,7 +27,6 @@ describe('ReactComponent', () => { React = require('react'); ReactDOM = require('react-dom'); ReactDOMServer = require('react-dom/server'); - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); ReactTestUtils = require('react-dom/test-utils'); }); @@ -493,4 +493,78 @@ describe('ReactComponent', () => { ' in Foo (at **)', ); }); + + if (ReactDOMFeatureFlags.useFiber) { + describe('with new features', () => { + beforeEach(() => { + require('ReactFeatureFlags').disableNewFiberFeatures = false; + }); + + it('warns on function as a return value from a function', () => { + function Foo() { + return Foo; + } + spyOn(console, 'error'); + var container = document.createElement('div'); + ReactDOM.render(, container); + expectDev(console.error.calls.count()).toBe(1); + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( + 'Warning: Functions are not valid as a React child. This may happen if ' + + 'you return a Component instead of from render. ' + + 'Or maybe you meant to call this function rather than return it.\n' + + ' in Foo (at **)', + ); + }); + + it('warns on function as a return value from a class', () => { + class Foo extends React.Component { + render() { + return Foo; + } + } + spyOn(console, 'error'); + var container = document.createElement('div'); + ReactDOM.render(, container); + expectDev(console.error.calls.count()).toBe(1); + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( + 'Warning: Functions are not valid as a React child. This may happen if ' + + 'you return a Component instead of from render. ' + + 'Or maybe you meant to call this function rather than return it.\n' + + ' in Foo (at **)', + ); + }); + + it('warns on function as a child to host component', () => { + function Foo() { + return
{Foo}
; + } + spyOn(console, 'error'); + var container = document.createElement('div'); + ReactDOM.render(, container); + expectDev(console.error.calls.count()).toBe(1); + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( + 'Warning: Functions are not valid as a React child. This may happen if ' + + 'you return a Component instead of from render. ' + + 'Or maybe you meant to call this function rather than return it.\n' + + ' in span (at **)\n' + + ' in div (at **)\n' + + ' in Foo (at **)', + ); + }); + + it('does not warn for function-as-a-child that gets resolved', () => { + function Bar(props) { + return props.children(); + } + function Foo() { + return {() => 'Hello'}; + } + spyOn(console, 'error'); + var container = document.createElement('div'); + ReactDOM.render(, container); + expect(container.innerHTML).toBe('Hello'); + expectDev(console.error.calls.count()).toBe(0); + }); + }); + } }); diff --git a/src/renderers/shared/fiber/ReactChildFiber.js b/src/renderers/shared/fiber/ReactChildFiber.js index 461ddd79ea388..9a4157b13e90c 100644 --- a/src/renderers/shared/fiber/ReactChildFiber.js +++ b/src/renderers/shared/fiber/ReactChildFiber.js @@ -200,6 +200,16 @@ function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) { } } +function warnOnFunctionType() { + warning( + false, + 'Functions are not valid as a React child. This may happen if ' + + 'you return a Component instead of from render. ' + + 'Or maybe you meant to call this function rather than return it.%s', + getCurrentFiberStackAddendum() || '', + ); +} + // This wrapper function exists because I expect to clone the code in each path // to be able to optimize each path individually by branching early. This needs // a compiler or we can do it manually. Helpers that don't need this branching @@ -565,6 +575,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) { throwOnInvalidObjectType(returnFiber, newChild); } + if (__DEV__) { + const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures; + if (!disableNewFiberFeatures && typeof newChild === 'function') { + warnOnFunctionType(); + } + } + return null; } @@ -638,6 +655,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) { throwOnInvalidObjectType(returnFiber, newChild); } + if (__DEV__) { + const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures; + if (!disableNewFiberFeatures && typeof newChild === 'function') { + warnOnFunctionType(); + } + } + return null; } @@ -697,6 +721,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) { throwOnInvalidObjectType(returnFiber, newChild); } + if (__DEV__) { + const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures; + if (!disableNewFiberFeatures && typeof newChild === 'function') { + warnOnFunctionType(); + } + } + return null; } @@ -1418,6 +1449,11 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) { throwOnInvalidObjectType(returnFiber, newChild); } + if (__DEV__) { + if (!disableNewFiberFeatures && typeof newChild === 'function') { + warnOnFunctionType(); + } + } if (!disableNewFiberFeatures && typeof newChild === 'undefined') { // If the new child is undefined, and the return fiber is a composite // component, throw an error. If Fiber return types are disabled,