diff --git a/src/renderers/__tests__/ReactComponent-test.js b/src/renderers/__tests__/ReactComponent-test.js index 30503913da052..dbea2bd3dcd5f 100644 --- a/src/renderers/__tests__/ReactComponent-test.js +++ b/src/renderers/__tests__/ReactComponent-test.js @@ -13,6 +13,7 @@ var React; var ReactDOM; +var ReactDOMServer; var ReactDOMFeatureFlags; var ReactTestUtils; @@ -24,6 +25,7 @@ describe('ReactComponent', () => { beforeEach(() => { React = require('react'); ReactDOM = require('react-dom'); + ReactDOMServer = require('react-dom/server'); ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); ReactTestUtils = require('react-dom/test-utils'); }); @@ -439,4 +441,56 @@ describe('ReactComponent', () => { ' in Foo (at **)', ); }); + + it('throws if a plain object is used as a child when using SSR', async () => { + var children = { + x: , + y: , + z: , + }; + var element =
{[children]}
; + var ex; + try { + ReactDOMServer.renderToString(element); + } catch (e) { + ex = e; + } + expect(ex).toBeDefined(); + expect(normalizeCodeLocInfo(ex.message)).toBe( + 'Objects are not valid as a React child (found: object with keys ' + + '{x, y, z}). If you meant to render a collection of children, use ' + + 'an array instead.' + + // Fiber gives a slightly better stack with the nearest host components + (ReactDOMFeatureFlags.useFiber ? '\n in div (at **)' : ''), + ); + }); + + it('throws if a plain object even if it is in an owner when using SSR', async () => { + class Foo extends React.Component { + render() { + var children = { + a: , + b: , + c: , + }; + return
{[children]}
; + } + } + var container = document.createElement('div'); + var ex; + try { + ReactDOMServer.renderToString(, container); + } catch (e) { + ex = e; + } + expect(ex).toBeDefined(); + expect(normalizeCodeLocInfo(ex.message)).toBe( + 'Objects are not valid as a React child (found: object with keys ' + + '{a, b, c}). If you meant to render a collection of children, use ' + + 'an array instead.\n' + + // Fiber gives a slightly better stack with the nearest host components + (ReactDOMFeatureFlags.useFiber ? ' in div (at **)\n' : '') + + ' in Foo (at **)', + ); + }); }); diff --git a/src/renderers/dom/ReactDOMNodeStreamRenderer.js b/src/renderers/dom/ReactDOMNodeStreamRenderer.js index 890538bb6fbd4..c5379be7290bd 100644 --- a/src/renderers/dom/ReactDOMNodeStreamRenderer.js +++ b/src/renderers/dom/ReactDOMNodeStreamRenderer.js @@ -14,6 +14,7 @@ var invariant = require('fbjs/lib/invariant'); var React = require('react'); var ReactPartialRenderer = require('ReactPartialRenderer'); +var ReactFeatureFlags = require('ReactFeatureFlags'); var Readable = require('stream').Readable; @@ -40,10 +41,13 @@ class ReactMarkupReadableStream extends Readable { * See https://facebook.github.io/react/docs/react-dom-stream.html#rendertostream */ function renderToStream(element) { - invariant( - React.isValidElement(element), - 'renderToStream(): You must pass a valid ReactElement.', - ); + const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures; + if (disableNewFiberFeatures) { + invariant( + React.isValidElement(element), + 'renderToStream(): You must pass a valid ReactElement.', + ); + } return new ReactMarkupReadableStream(element, false); } @@ -53,10 +57,13 @@ function renderToStream(element) { * See https://facebook.github.io/react/docs/react-dom-stream.html#rendertostaticstream */ function renderToStaticStream(element) { - invariant( - React.isValidElement(element), - 'renderToStaticStream(): You must pass a valid ReactElement.', - ); + const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures; + if (disableNewFiberFeatures) { + invariant( + React.isValidElement(element), + 'renderToStaticStream(): You must pass a valid ReactElement.', + ); + } return new ReactMarkupReadableStream(element, true); } diff --git a/src/renderers/dom/ReactDOMStringRenderer.js b/src/renderers/dom/ReactDOMStringRenderer.js index 7995ac3581bcd..7461c3297c837 100644 --- a/src/renderers/dom/ReactDOMStringRenderer.js +++ b/src/renderers/dom/ReactDOMStringRenderer.js @@ -14,6 +14,7 @@ var invariant = require('fbjs/lib/invariant'); var React = require('react'); var ReactPartialRenderer = require('ReactPartialRenderer'); +var ReactFeatureFlags = require('ReactFeatureFlags'); /** * Render a ReactElement to its initial HTML. This should only be used on the @@ -21,10 +22,13 @@ var ReactPartialRenderer = require('ReactPartialRenderer'); * See https://facebook.github.io/react/docs/react-dom-server.html#rendertostring */ function renderToString(element) { - invariant( - React.isValidElement(element), - 'renderToString(): You must pass a valid ReactElement.', - ); + const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures; + if (disableNewFiberFeatures) { + invariant( + React.isValidElement(element), + 'renderToString(): You must pass a valid ReactElement.', + ); + } var renderer = new ReactPartialRenderer(element, false); var markup = renderer.read(Infinity); return markup; @@ -36,10 +40,13 @@ function renderToString(element) { * See https://facebook.github.io/react/docs/react-dom-server.html#rendertostaticmarkup */ function renderToStaticMarkup(element) { - invariant( - React.isValidElement(element), - 'renderToStaticMarkup(): You must pass a valid ReactElement.', - ); + const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures; + if (disableNewFiberFeatures) { + invariant( + React.isValidElement(element), + 'renderToStaticMarkup(): You must pass a valid ReactElement.', + ); + } var renderer = new ReactPartialRenderer(element, true); var markup = renderer.read(Infinity); return markup; diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js index 6f74ed2b48f30..d7f8fe3ee2032 100644 --- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js +++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js @@ -332,6 +332,19 @@ describe('ReactDOMServerIntegration', () => { }); describe('basic rendering', function() { + beforeEach(() => { + onAfterResetModules = () => { + const ReactFeatureFlags = require('ReactFeatureFlags'); + ReactFeatureFlags.disableNewFiberFeatures = false; + }; + + resetModules(); + }); + + afterEach(() => { + onAfterResetModules = null; + }); + itRenders('a blank div', async render => { const e = await render(
); expect(e.tagName).toBe('DIV'); @@ -347,6 +360,70 @@ describe('ReactDOMServerIntegration', () => { expect(e.childNodes.length).toBe(1); expect(e.firstChild.tagName).toBe('BR'); }); + + if (ReactDOMFeatureFlags.useFiber) { + itRenders('a array type children as a child', async render => { + let Header = props => { + return

header

; + }; + let Footer = props => { + return [

footer

,

about

]; + }; + let e = await render([ +
text1
, + text2, +
, +