mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Make ReactDOM.createPortal() official (#10675)
* Validate portal container early * Add ReactDOM.createPortal but leave unstable_ alias usable
This commit is contained in:
@@ -522,10 +522,7 @@ describe('ReactUpdates', () => {
|
||||
var portal = null;
|
||||
// If we're using Fiber, we use Portals instead to achieve this.
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
portal = ReactDOM.unstable_createPortal(
|
||||
<B ref={n => (b = n)} />,
|
||||
bContainer,
|
||||
);
|
||||
portal = ReactDOM.createPortal(<B ref={n => (b = n)} />, bContainer);
|
||||
}
|
||||
return <div>A{this.state.x}{portal}</div>;
|
||||
}
|
||||
|
||||
@@ -246,10 +246,7 @@ describe('ReactDOMProduction', () => {
|
||||
var expectSVG = {ref: el => svgEls.push(el)};
|
||||
var expectHTML = {ref: el => htmlEls.push(el)};
|
||||
var usePortal = function(tree) {
|
||||
return ReactDOM.unstable_createPortal(
|
||||
tree,
|
||||
document.createElement('div'),
|
||||
);
|
||||
return ReactDOM.createPortal(tree, document.createElement('div'));
|
||||
};
|
||||
var assertNamespacesMatch = function(tree) {
|
||||
var container = document.createElement('div');
|
||||
|
||||
@@ -646,7 +646,22 @@ function renderSubtreeIntoContainer(
|
||||
return DOMRenderer.getPublicRootInstance(root);
|
||||
}
|
||||
|
||||
function createPortal(
|
||||
children: ReactNodeList,
|
||||
container: DOMContainer,
|
||||
key: ?string = null,
|
||||
) {
|
||||
invariant(
|
||||
isValidContainer(container),
|
||||
'Target container is not a DOM element.',
|
||||
);
|
||||
// TODO: pass ReactDOM portal implementation as third argument
|
||||
return ReactPortal.createPortal(children, container, null, key);
|
||||
}
|
||||
|
||||
var ReactDOMFiber = {
|
||||
createPortal,
|
||||
|
||||
hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
|
||||
// TODO: throw or warn if we couldn't hydrate?
|
||||
return renderSubtreeIntoContainer(null, element, container, true, callback);
|
||||
@@ -742,14 +757,9 @@ var ReactDOMFiber = {
|
||||
|
||||
findDOMNode: findDOMNode,
|
||||
|
||||
unstable_createPortal(
|
||||
children: ReactNodeList,
|
||||
container: DOMContainer,
|
||||
key: ?string = null,
|
||||
) {
|
||||
// TODO: pass ReactDOM portal implementation as third argument
|
||||
return ReactPortal.createPortal(children, container, null, key);
|
||||
},
|
||||
// Temporary alias since we already shipped React 16 RC with it.
|
||||
// TODO: remove in React 17.
|
||||
unstable_createPortal: createPortal,
|
||||
|
||||
unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
|
||||
|
||||
|
||||
@@ -182,10 +182,7 @@ describe('ReactDOMFiber', () => {
|
||||
var expectMath = {ref: el => mathEls.push(el)};
|
||||
|
||||
var usePortal = function(tree) {
|
||||
return ReactDOM.unstable_createPortal(
|
||||
tree,
|
||||
document.createElement('div'),
|
||||
);
|
||||
return ReactDOM.createPortal(tree, document.createElement('div'));
|
||||
};
|
||||
|
||||
var assertNamespacesMatch = function(tree) {
|
||||
@@ -212,6 +209,24 @@ describe('ReactDOMFiber', () => {
|
||||
it('should render one portal', () => {
|
||||
var portalContainer = document.createElement('div');
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
{ReactDOM.createPortal(<div>portal</div>, portalContainer)}
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
expect(portalContainer.innerHTML).toBe('<div>portal</div>');
|
||||
expect(container.innerHTML).toBe('<div></div>');
|
||||
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
expect(portalContainer.innerHTML).toBe('');
|
||||
expect(container.innerHTML).toBe('');
|
||||
});
|
||||
|
||||
// TODO: remove in React 17
|
||||
it('should support unstable_createPortal alias', () => {
|
||||
var portalContainer = document.createElement('div');
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
{ReactDOM.unstable_createPortal(<div>portal</div>, portalContainer)}
|
||||
@@ -260,12 +275,12 @@ describe('ReactDOMFiber', () => {
|
||||
const {step} = this.props;
|
||||
return [
|
||||
<Child key="a" name={`normal[0]:${step}`} />,
|
||||
ReactDOM.unstable_createPortal(
|
||||
ReactDOM.createPortal(
|
||||
<Child key="b" name={`portal1[0]:${step}`} />,
|
||||
portalContainer1,
|
||||
),
|
||||
<Child key="c" name={`normal[1]:${step}`} />,
|
||||
ReactDOM.unstable_createPortal(
|
||||
ReactDOM.createPortal(
|
||||
[
|
||||
<Child key="d" name={`portal2[0]:${step}`} />,
|
||||
<Child key="e" name={`portal2[1]:${step}`} />,
|
||||
@@ -334,14 +349,14 @@ describe('ReactDOMFiber', () => {
|
||||
ReactDOM.render(
|
||||
[
|
||||
<div key="a">normal[0]</div>,
|
||||
ReactDOM.unstable_createPortal(
|
||||
ReactDOM.createPortal(
|
||||
[
|
||||
<div key="b">portal1[0]</div>,
|
||||
ReactDOM.unstable_createPortal(
|
||||
ReactDOM.createPortal(
|
||||
<div key="c">portal2[0]</div>,
|
||||
portalContainer2,
|
||||
),
|
||||
ReactDOM.unstable_createPortal(
|
||||
ReactDOM.createPortal(
|
||||
<div key="d">portal3[0]</div>,
|
||||
portalContainer3,
|
||||
),
|
||||
@@ -374,7 +389,7 @@ describe('ReactDOMFiber', () => {
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
{ReactDOM.unstable_createPortal(<div>portal:1</div>, portalContainer)}
|
||||
{ReactDOM.createPortal(<div>portal:1</div>, portalContainer)}
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
@@ -383,7 +398,7 @@ describe('ReactDOMFiber', () => {
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
{ReactDOM.unstable_createPortal(<div>portal:2</div>, portalContainer)}
|
||||
{ReactDOM.createPortal(<div>portal:2</div>, portalContainer)}
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
@@ -392,7 +407,7 @@ describe('ReactDOMFiber', () => {
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
{ReactDOM.unstable_createPortal(<p>portal:3</p>, portalContainer)}
|
||||
{ReactDOM.createPortal(<p>portal:3</p>, portalContainer)}
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
@@ -401,7 +416,7 @@ describe('ReactDOMFiber', () => {
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
{ReactDOM.unstable_createPortal(['Hi', 'Bye'], portalContainer)}
|
||||
{ReactDOM.createPortal(['Hi', 'Bye'], portalContainer)}
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
@@ -410,7 +425,7 @@ describe('ReactDOMFiber', () => {
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
{ReactDOM.unstable_createPortal(['Bye', 'Hi'], portalContainer)}
|
||||
{ReactDOM.createPortal(['Bye', 'Hi'], portalContainer)}
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
@@ -419,7 +434,7 @@ describe('ReactDOMFiber', () => {
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
{ReactDOM.unstable_createPortal(null, portalContainer)}
|
||||
{ReactDOM.createPortal(null, portalContainer)}
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
@@ -700,7 +715,7 @@ describe('ReactDOMFiber', () => {
|
||||
}
|
||||
|
||||
render() {
|
||||
return ReactDOM.unstable_createPortal(<Component />, portalContainer);
|
||||
return ReactDOM.createPortal(<Component />, portalContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,7 +756,7 @@ describe('ReactDOMFiber', () => {
|
||||
}
|
||||
|
||||
render() {
|
||||
return ReactDOM.unstable_createPortal(<Component />, portalContainer);
|
||||
return ReactDOM.createPortal(<Component />, portalContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -781,7 +796,7 @@ describe('ReactDOMFiber', () => {
|
||||
}
|
||||
|
||||
render() {
|
||||
return ReactDOM.unstable_createPortal(<Component />, portalContainer);
|
||||
return ReactDOM.createPortal(<Component />, portalContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -821,7 +836,7 @@ describe('ReactDOMFiber', () => {
|
||||
|
||||
ReactDOM.render(
|
||||
<div onClick={() => ops.push('parent clicked')}>
|
||||
{ReactDOM.unstable_createPortal(
|
||||
{ReactDOM.createPortal(
|
||||
<div
|
||||
onClick={() => ops.push('portal clicked')}
|
||||
ref={n => (portal = n)}>
|
||||
@@ -874,7 +889,7 @@ describe('ReactDOMFiber', () => {
|
||||
onMouseEnter={() => ops.push('enter parent')}
|
||||
onMouseLeave={() => ops.push('leave parent')}>
|
||||
<div ref={n => (firstTarget = n)} />
|
||||
{ReactDOM.unstable_createPortal(
|
||||
{ReactDOM.createPortal(
|
||||
<div
|
||||
onMouseEnter={() => ops.push('enter portal')}
|
||||
onMouseLeave={() => ops.push('leave portal')}
|
||||
@@ -909,6 +924,15 @@ describe('ReactDOMFiber', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should throw on bad createPortal argument', () => {
|
||||
expect(() => {
|
||||
ReactDOM.createPortal(<div>portal</div>, null);
|
||||
}).toThrow('Target container is not a DOM element.');
|
||||
expect(() => {
|
||||
ReactDOM.createPortal(<div>portal</div>, document.createTextNode('hi'));
|
||||
}).toThrow('Target container is not a DOM element.');
|
||||
});
|
||||
|
||||
it('should warn for non-functional event listeners', () => {
|
||||
spyOn(console, 'error');
|
||||
class Example extends React.Component {
|
||||
|
||||
@@ -319,7 +319,7 @@ describe('renderSubtreeIntoContainer', () => {
|
||||
'a React 15 tree inside a React 16 tree using ' +
|
||||
"unstable_renderSubtreeIntoContainer, which isn't supported. Try to " +
|
||||
'make sure you have only one copy of React (and ideally, switch to ' +
|
||||
'ReactDOM.unstable_createPortal).',
|
||||
'ReactDOM.createPortal).',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ const ReactNativeFiber: ReactNativeType = {
|
||||
UIManager.removeRootView(containerTag);
|
||||
},
|
||||
|
||||
unstable_createPortal(
|
||||
createPortal(
|
||||
children: ReactNodeList,
|
||||
containerTag: number,
|
||||
key: ?string = null,
|
||||
|
||||
@@ -71,7 +71,7 @@ if (__DEV__) {
|
||||
'a React 15 tree inside a React 16 tree using ' +
|
||||
"unstable_renderSubtreeIntoContainer, which isn't supported. Try " +
|
||||
'to make sure you have only one copy of React (and ideally, switch ' +
|
||||
'to ReactDOM.unstable_createPortal).',
|
||||
'to ReactDOM.createPortal).',
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user