Skip to content

Commit 0e8825b

Browse files
gaearonmadeinfree
authored andcommitted
Prevent infinite loop when SSR-rendering a portal (facebook#11709)
1 parent 51cf127 commit 0e8825b

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

packages/react-dom/src/__tests__/ReactServerRendering-test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
'use strict';
1111

1212
var React;
13+
var ReactCallReturn;
1314
var ReactDOM;
1415
var ReactDOMServer;
1516
var ReactTestUtils;
@@ -23,6 +24,7 @@ describe('ReactDOMServer', () => {
2324
beforeEach(() => {
2425
jest.resetModules();
2526
React = require('react');
27+
ReactCallReturn = require('react-call-return');
2628
ReactDOM = require('react-dom');
2729
ReactTestUtils = require('react-dom/test-utils');
2830
PropTypes = require('prop-types');
@@ -772,4 +774,36 @@ describe('ReactDOMServer', () => {
772774
);
773775
}
774776
});
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+
});
775809
});

packages/react-dom/src/server/ReactPartialRenderer.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ import warning from 'fbjs/lib/warning';
1919
import checkPropTypes from 'prop-types/checkPropTypes';
2020
import describeComponentFrame from 'shared/describeComponentFrame';
2121
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';
2328

2429
import {
2530
createMarkupForCustomAttribute,
@@ -585,6 +590,27 @@ class ReactDOMServerRenderer {
585590
if (nextChild === null || nextChild === false) {
586591
return '';
587592
} 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+
}
588614
const nextChildren = toArray(nextChild);
589615
const frame: Frame = {
590616
domNamespace: parentNamespace,

0 commit comments

Comments
 (0)