@samitouri / QOS-React / commits / c3f1b6cd91

Prevent infinite loop when SSR-rendering a portal (#11709)

Dan Abramov committed Nov 29, 2017 at 21:45 UTC c3f1b6cd91ffa3e05465c3ed2f48225876c1bd3b
2 files changed +61 -1
packages/react-dom/src/__tests__/ReactServerRendering-test.js
+34
@@ -10,6 +10,7 @@
10 'use strict';
11
12 var React;
13 +var ReactCallReturn;
14 var ReactDOM;
15 var ReactDOMServer;
16 var ReactTestUtils;
@@ -23,6 +24,7 @@ describe('ReactDOMServer', () => {
24 beforeEach(() => {
25 jest.resetModules();
26 React = require('react');
27 + ReactCallReturn = require('react-call-return');
28 ReactDOM = require('react-dom');
29 ReactTestUtils = require('react-dom/test-utils');
30 PropTypes = require('prop-types');
@@ -772,4 +774,36 @@ describe('ReactDOMServer', () => {
774 );
775 }
776 });
777 +
778 + it('should throw rendering portals on the server', () => {
779 + var div = document.createElement('div');
780 + expect(() => {
781 + ReactDOMServer.renderToString(
782 + <div>{ReactDOM.createPortal(<div />, div)}</div>,
783 + );
784 + }).toThrow(
785 + 'Portals are not currently supported by the server renderer. ' +
786 + 'Render them conditionally so that they only appear on the client render.',
787 + );
788 + });
789 +
790 + it('should throw rendering call/return on the server', () => {
791 + var div = document.createElement('div');
792 + expect(() => {
793 + ReactDOMServer.renderToString(
794 + <div>{ReactCallReturn.unstable_createReturn(42)}</div>,
795 + );
796 + }).toThrow(
797 + 'The experimental Call and Return types are not currently supported by the server renderer.',
798 + );
799 + expect(() => {
800 + ReactDOMServer.renderToString(
801 + <div>
802 + {ReactCallReturn.unstable_createCall(null, function() {}, {})}
803 + </div>,
804 + );
805 + }).toThrow(
806 + 'The experimental Call and Return types are not currently supported by the server renderer.',
807 + );
808 + });
809 });
packages/react-dom/src/server/ReactPartialRenderer.js
+27 -1
@@ -19,7 +19,12 @@ import warning from 'fbjs/lib/warning';
19 import checkPropTypes from 'prop-types/checkPropTypes';
20 import describeComponentFrame from 'shared/describeComponentFrame';
21 import {ReactDebugCurrentFrame} from 'shared/ReactGlobalSharedState';
22 -import {REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
22 +import {
23 + REACT_FRAGMENT_TYPE,
24 + REACT_CALL_TYPE,
25 + REACT_RETURN_TYPE,
26 + REACT_PORTAL_TYPE,
27 +} from 'shared/ReactSymbols';
28
29 import {
30 createMarkupForCustomAttribute,
@@ -585,6 +590,27 @@ class ReactDOMServerRenderer {
590 if (nextChild === null || nextChild === false) {
591 return '';
592 } else if (!React.isValidElement(nextChild)) {
593 + if (nextChild != null && nextChild.$$typeof != null) {
594 + // Catch unexpected special types early.
595 + const $$typeof = nextChild.$$typeof;
596 + invariant(
597 + $$typeof !== REACT_PORTAL_TYPE,
598 + 'Portals are not currently supported by the server renderer. ' +
599 + 'Render them conditionally so that they only appear on the client render.',
600 + );
601 + invariant(
602 + $$typeof !== REACT_CALL_TYPE && $$typeof !== REACT_RETURN_TYPE,
603 + 'The experimental Call and Return types are not currently ' +
604 + 'supported by the server renderer.',
605 + );
606 + // Catch-all to prevent an infinite loop if React.Children.toArray() supports some new type.
607 + invariant(
608 + false,
609 + 'Unknown element-like object type: %s. This is likely a bug in React. ' +
610 + 'Please file an issue.',
611 + ($$typeof: any).toString(),
612 + );
613 + }
614 const nextChildren = toArray(nextChild);
615 const frame: Frame = {
616 domNamespace: parentNamespace,