diff --git a/packages/react-dom/src/__tests__/ReactServerRendering-test.js b/packages/react-dom/src/__tests__/ReactServerRendering-test.js index 12294a9e699ee..9bb4c6129444e 100644 --- a/packages/react-dom/src/__tests__/ReactServerRendering-test.js +++ b/packages/react-dom/src/__tests__/ReactServerRendering-test.js @@ -10,6 +10,7 @@ 'use strict'; var React; +var ReactCallReturn; var ReactDOM; var ReactDOMServer; var ReactTestUtils; @@ -23,6 +24,7 @@ describe('ReactDOMServer', () => { beforeEach(() => { jest.resetModules(); React = require('react'); + ReactCallReturn = require('react-call-return'); ReactDOM = require('react-dom'); ReactTestUtils = require('react-dom/test-utils'); PropTypes = require('prop-types'); @@ -772,4 +774,36 @@ describe('ReactDOMServer', () => { ); } }); + + it('should throw rendering portals on the server', () => { + var div = document.createElement('div'); + expect(() => { + ReactDOMServer.renderToString( +
{ReactDOM.createPortal(
, div)}
, + ); + }).toThrow( + 'Portals are not currently supported by the server renderer. ' + + 'Render them conditionally so that they only appear on the client render.', + ); + }); + + it('should throw rendering call/return on the server', () => { + var div = document.createElement('div'); + expect(() => { + ReactDOMServer.renderToString( +
{ReactCallReturn.unstable_createReturn(42)}
, + ); + }).toThrow( + 'The experimental Call and Return types are not currently supported by the server renderer.', + ); + expect(() => { + ReactDOMServer.renderToString( +
+ {ReactCallReturn.unstable_createCall(null, function() {}, {})} +
, + ); + }).toThrow( + 'The experimental Call and Return types are not currently supported by the server renderer.', + ); + }); }); diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index c76a311aac3b7..e05ccec206941 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -19,7 +19,12 @@ import warning from 'fbjs/lib/warning'; import checkPropTypes from 'prop-types/checkPropTypes'; import describeComponentFrame from 'shared/describeComponentFrame'; import {ReactDebugCurrentFrame} from 'shared/ReactGlobalSharedState'; -import {REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols'; +import { + REACT_FRAGMENT_TYPE, + REACT_CALL_TYPE, + REACT_RETURN_TYPE, + REACT_PORTAL_TYPE, +} from 'shared/ReactSymbols'; import { createMarkupForCustomAttribute, @@ -585,6 +590,27 @@ class ReactDOMServerRenderer { if (nextChild === null || nextChild === false) { return ''; } else if (!React.isValidElement(nextChild)) { + if (nextChild != null && nextChild.$$typeof != null) { + // Catch unexpected special types early. + const $$typeof = nextChild.$$typeof; + invariant( + $$typeof !== REACT_PORTAL_TYPE, + 'Portals are not currently supported by the server renderer. ' + + 'Render them conditionally so that they only appear on the client render.', + ); + invariant( + $$typeof !== REACT_CALL_TYPE && $$typeof !== REACT_RETURN_TYPE, + 'The experimental Call and Return types are not currently ' + + 'supported by the server renderer.', + ); + // Catch-all to prevent an infinite loop if React.Children.toArray() supports some new type. + invariant( + false, + 'Unknown element-like object type: %s. This is likely a bug in React. ' + + 'Please file an issue.', + ($$typeof: any).toString(), + ); + } const nextChildren = toArray(nextChild); const frame: Frame = { domNamespace: parentNamespace,