Fix crash unmounting an empty Portal (#14820)
* Adds failing test for https://github.com/facebook/react/issues/14811 * Fix removeChild() crash when removing an empty Portal
Dan Abramov committed
Feb 11, 2019 at 18:37 UTC
1fecba92307041e181ce425082d4d21ec8928728
2 files changed
+39
-5
packages/react-dom/src/__tests__/ReactDOMFiber-test.js
+34
@@ -461,6 +461,40 @@ describe('ReactDOMFiber', () => {
461
expect(container.innerHTML).toBe('<div></div>');
462
});
463
464
+ it('should unmount empty portal component wherever it appears', () => {
465
+ const portalContainer = document.createElement('div');
466
+
467
+ class Wrapper extends React.Component {
468
+ constructor(props) {
469
+ super(props);
470
+ this.state = {
471
+ show: true,
472
+ };
473
+ }
474
+ render() {
475
+ return (
476
+ <div>
477
+ {this.state.show && (
478
+ <React.Fragment>
479
+ {ReactDOM.createPortal(null, portalContainer)}
480
+ <div>child</div>
481
+ </React.Fragment>
482
+ )}
483
+ <div>parent</div>
484
+ </div>
485
+ );
486
+ }
487
+ }
488
+
489
+ const instance = ReactDOM.render(<Wrapper />, container);
490
+ expect(container.innerHTML).toBe(
491
+ '<div><div>child</div><div>parent</div></div>',
492
+ );
493
+ instance.setState({show: false});
494
+ expect(instance.state.show).toBe(false);
495
+ expect(container.innerHTML).toBe('<div><div>parent</div></div>');
496
+ });
497
+
498
it('should keep track of namespace across portals (simple)', () => {
499
assertNamespacesMatch(
500
<svg {...expectSVG}>
packages/react-reconciler/src/ReactFiberCommitWork.js
+5
-5
@@ -1038,12 +1038,12 @@ function unmountHostComponents(current): void {
1038
}
1039
// Don't visit children because we already visited them.
1040
} else if (node.tag === HostPortal) {
1041
- // When we go into a portal, it becomes the parent to remove from.
1042
- // We will reassign it back when we pop the portal on the way up.
1043
- currentParent = node.stateNode.containerInfo;
1044
- currentParentIsContainer = true;
1045
- // Visit children because portals might contain host components.
1041
if (node.child !== null) {
1042
+ // When we go into a portal, it becomes the parent to remove from.
1043
+ // We will reassign it back when we pop the portal on the way up.
1044
+ currentParent = node.stateNode.containerInfo;
1045
+ currentParentIsContainer = true;
1046
+ // Visit children because portals might contain host components.
1047
node.child.return = node;
1048
node = node.child;
1049
continue;