Skip to content

Commit 89508f2

Browse files
authored
Make ReactDOM.createPortal() official (#10675)
* Validate portal container early * Add ReactDOM.createPortal but leave unstable_ alias usable
1 parent f74981b commit 89508f2

File tree

7 files changed

+67
-39
lines changed

7 files changed

+67
-39
lines changed

src/renderers/__tests__/ReactUpdates-test.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -522,10 +522,7 @@ describe('ReactUpdates', () => {
522522
var portal = null;
523523
// If we're using Fiber, we use Portals instead to achieve this.
524524
if (ReactDOMFeatureFlags.useFiber) {
525-
portal = ReactDOM.unstable_createPortal(
526-
<B ref={n => (b = n)} />,
527-
bContainer,
528-
);
525+
portal = ReactDOM.createPortal(<B ref={n => (b = n)} />, bContainer);
529526
}
530527
return <div>A{this.state.x}{portal}</div>;
531528
}

src/renderers/dom/__tests__/ReactDOMProduction-test.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,7 @@ describe('ReactDOMProduction', () => {
246246
var expectSVG = {ref: el => svgEls.push(el)};
247247
var expectHTML = {ref: el => htmlEls.push(el)};
248248
var usePortal = function(tree) {
249-
return ReactDOM.unstable_createPortal(
250-
tree,
251-
document.createElement('div'),
252-
);
249+
return ReactDOM.createPortal(tree, document.createElement('div'));
253250
};
254251
var assertNamespacesMatch = function(tree) {
255252
var container = document.createElement('div');

src/renderers/dom/fiber/ReactDOMFiberEntry.js

+18-8
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,22 @@ function renderSubtreeIntoContainer(
646646
return DOMRenderer.getPublicRootInstance(root);
647647
}
648648

649+
function createPortal(
650+
children: ReactNodeList,
651+
container: DOMContainer,
652+
key: ?string = null,
653+
) {
654+
invariant(
655+
isValidContainer(container),
656+
'Target container is not a DOM element.',
657+
);
658+
// TODO: pass ReactDOM portal implementation as third argument
659+
return ReactPortal.createPortal(children, container, null, key);
660+
}
661+
649662
var ReactDOMFiber = {
663+
createPortal,
664+
650665
hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
651666
// TODO: throw or warn if we couldn't hydrate?
652667
return renderSubtreeIntoContainer(null, element, container, true, callback);
@@ -742,14 +757,9 @@ var ReactDOMFiber = {
742757

743758
findDOMNode: findDOMNode,
744759

745-
unstable_createPortal(
746-
children: ReactNodeList,
747-
container: DOMContainer,
748-
key: ?string = null,
749-
) {
750-
// TODO: pass ReactDOM portal implementation as third argument
751-
return ReactPortal.createPortal(children, container, null, key);
752-
},
760+
// Temporary alias since we already shipped React 16 RC with it.
761+
// TODO: remove in React 17.
762+
unstable_createPortal: createPortal,
753763

754764
unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
755765

src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js

+44-20
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,7 @@ describe('ReactDOMFiber', () => {
182182
var expectMath = {ref: el => mathEls.push(el)};
183183

184184
var usePortal = function(tree) {
185-
return ReactDOM.unstable_createPortal(
186-
tree,
187-
document.createElement('div'),
188-
);
185+
return ReactDOM.createPortal(tree, document.createElement('div'));
189186
};
190187

191188
var assertNamespacesMatch = function(tree) {
@@ -212,6 +209,24 @@ describe('ReactDOMFiber', () => {
212209
it('should render one portal', () => {
213210
var portalContainer = document.createElement('div');
214211

212+
ReactDOM.render(
213+
<div>
214+
{ReactDOM.createPortal(<div>portal</div>, portalContainer)}
215+
</div>,
216+
container,
217+
);
218+
expect(portalContainer.innerHTML).toBe('<div>portal</div>');
219+
expect(container.innerHTML).toBe('<div></div>');
220+
221+
ReactDOM.unmountComponentAtNode(container);
222+
expect(portalContainer.innerHTML).toBe('');
223+
expect(container.innerHTML).toBe('');
224+
});
225+
226+
// TODO: remove in React 17
227+
it('should support unstable_createPortal alias', () => {
228+
var portalContainer = document.createElement('div');
229+
215230
ReactDOM.render(
216231
<div>
217232
{ReactDOM.unstable_createPortal(<div>portal</div>, portalContainer)}
@@ -260,12 +275,12 @@ describe('ReactDOMFiber', () => {
260275
const {step} = this.props;
261276
return [
262277
<Child key="a" name={`normal[0]:${step}`} />,
263-
ReactDOM.unstable_createPortal(
278+
ReactDOM.createPortal(
264279
<Child key="b" name={`portal1[0]:${step}`} />,
265280
portalContainer1,
266281
),
267282
<Child key="c" name={`normal[1]:${step}`} />,
268-
ReactDOM.unstable_createPortal(
283+
ReactDOM.createPortal(
269284
[
270285
<Child key="d" name={`portal2[0]:${step}`} />,
271286
<Child key="e" name={`portal2[1]:${step}`} />,
@@ -334,14 +349,14 @@ describe('ReactDOMFiber', () => {
334349
ReactDOM.render(
335350
[
336351
<div key="a">normal[0]</div>,
337-
ReactDOM.unstable_createPortal(
352+
ReactDOM.createPortal(
338353
[
339354
<div key="b">portal1[0]</div>,
340-
ReactDOM.unstable_createPortal(
355+
ReactDOM.createPortal(
341356
<div key="c">portal2[0]</div>,
342357
portalContainer2,
343358
),
344-
ReactDOM.unstable_createPortal(
359+
ReactDOM.createPortal(
345360
<div key="d">portal3[0]</div>,
346361
portalContainer3,
347362
),
@@ -374,7 +389,7 @@ describe('ReactDOMFiber', () => {
374389

375390
ReactDOM.render(
376391
<div>
377-
{ReactDOM.unstable_createPortal(<div>portal:1</div>, portalContainer)}
392+
{ReactDOM.createPortal(<div>portal:1</div>, portalContainer)}
378393
</div>,
379394
container,
380395
);
@@ -383,7 +398,7 @@ describe('ReactDOMFiber', () => {
383398

384399
ReactDOM.render(
385400
<div>
386-
{ReactDOM.unstable_createPortal(<div>portal:2</div>, portalContainer)}
401+
{ReactDOM.createPortal(<div>portal:2</div>, portalContainer)}
387402
</div>,
388403
container,
389404
);
@@ -392,7 +407,7 @@ describe('ReactDOMFiber', () => {
392407

393408
ReactDOM.render(
394409
<div>
395-
{ReactDOM.unstable_createPortal(<p>portal:3</p>, portalContainer)}
410+
{ReactDOM.createPortal(<p>portal:3</p>, portalContainer)}
396411
</div>,
397412
container,
398413
);
@@ -401,7 +416,7 @@ describe('ReactDOMFiber', () => {
401416

402417
ReactDOM.render(
403418
<div>
404-
{ReactDOM.unstable_createPortal(['Hi', 'Bye'], portalContainer)}
419+
{ReactDOM.createPortal(['Hi', 'Bye'], portalContainer)}
405420
</div>,
406421
container,
407422
);
@@ -410,7 +425,7 @@ describe('ReactDOMFiber', () => {
410425

411426
ReactDOM.render(
412427
<div>
413-
{ReactDOM.unstable_createPortal(['Bye', 'Hi'], portalContainer)}
428+
{ReactDOM.createPortal(['Bye', 'Hi'], portalContainer)}
414429
</div>,
415430
container,
416431
);
@@ -419,7 +434,7 @@ describe('ReactDOMFiber', () => {
419434

420435
ReactDOM.render(
421436
<div>
422-
{ReactDOM.unstable_createPortal(null, portalContainer)}
437+
{ReactDOM.createPortal(null, portalContainer)}
423438
</div>,
424439
container,
425440
);
@@ -700,7 +715,7 @@ describe('ReactDOMFiber', () => {
700715
}
701716

702717
render() {
703-
return ReactDOM.unstable_createPortal(<Component />, portalContainer);
718+
return ReactDOM.createPortal(<Component />, portalContainer);
704719
}
705720
}
706721

@@ -741,7 +756,7 @@ describe('ReactDOMFiber', () => {
741756
}
742757

743758
render() {
744-
return ReactDOM.unstable_createPortal(<Component />, portalContainer);
759+
return ReactDOM.createPortal(<Component />, portalContainer);
745760
}
746761
}
747762

@@ -781,7 +796,7 @@ describe('ReactDOMFiber', () => {
781796
}
782797

783798
render() {
784-
return ReactDOM.unstable_createPortal(<Component />, portalContainer);
799+
return ReactDOM.createPortal(<Component />, portalContainer);
785800
}
786801
}
787802

@@ -821,7 +836,7 @@ describe('ReactDOMFiber', () => {
821836

822837
ReactDOM.render(
823838
<div onClick={() => ops.push('parent clicked')}>
824-
{ReactDOM.unstable_createPortal(
839+
{ReactDOM.createPortal(
825840
<div
826841
onClick={() => ops.push('portal clicked')}
827842
ref={n => (portal = n)}>
@@ -874,7 +889,7 @@ describe('ReactDOMFiber', () => {
874889
onMouseEnter={() => ops.push('enter parent')}
875890
onMouseLeave={() => ops.push('leave parent')}>
876891
<div ref={n => (firstTarget = n)} />
877-
{ReactDOM.unstable_createPortal(
892+
{ReactDOM.createPortal(
878893
<div
879894
onMouseEnter={() => ops.push('enter portal')}
880895
onMouseLeave={() => ops.push('leave portal')}
@@ -909,6 +924,15 @@ describe('ReactDOMFiber', () => {
909924
]);
910925
});
911926

927+
it('should throw on bad createPortal argument', () => {
928+
expect(() => {
929+
ReactDOM.createPortal(<div>portal</div>, null);
930+
}).toThrow('Target container is not a DOM element.');
931+
expect(() => {
932+
ReactDOM.createPortal(<div>portal</div>, document.createTextNode('hi'));
933+
}).toThrow('Target container is not a DOM element.');
934+
});
935+
912936
it('should warn for non-functional event listeners', () => {
913937
spyOn(console, 'error');
914938
class Example extends React.Component {

src/renderers/dom/shared/__tests__/renderSubtreeIntoContainer-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ describe('renderSubtreeIntoContainer', () => {
319319
'a React 15 tree inside a React 16 tree using ' +
320320
"unstable_renderSubtreeIntoContainer, which isn't supported. Try to " +
321321
'make sure you have only one copy of React (and ideally, switch to ' +
322-
'ReactDOM.unstable_createPortal).',
322+
'ReactDOM.createPortal).',
323323
);
324324
});
325325
}

src/renderers/native/ReactNativeFiberEntry.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ const ReactNativeFiber: ReactNativeType = {
7979
UIManager.removeRootView(containerTag);
8080
},
8181

82-
unstable_createPortal(
82+
createPortal(
8383
children: ReactNodeList,
8484
containerTag: number,
8585
key: ?string = null,

src/renderers/shared/fiber/ReactFiberClassComponent.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ if (__DEV__) {
7171
'a React 15 tree inside a React 16 tree using ' +
7272
"unstable_renderSubtreeIntoContainer, which isn't supported. Try " +
7373
'to make sure you have only one copy of React (and ideally, switch ' +
74-
'to ReactDOM.unstable_createPortal).',
74+
'to ReactDOM.createPortal).',
7575
);
7676
},
7777
});

0 commit comments

Comments
 (0)