Skip to content

Commit 85ada76

Browse files
committed
Handle OutPortal updates between portal nodes
Previously, OutPortal could happily unmount and remount nodes, and switch between them if there was an unmount en route, but it would crash if we switched directly, because the previous node wasn't unmounted first.
1 parent 3d42f1d commit 85ada76

File tree

2 files changed

+48
-0
lines changed

2 files changed

+48
-0
lines changed

src/index.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ type OutPortalProps<C extends Component<any>> = {
120120
export class OutPortal<C extends Component<any>> extends React.PureComponent<OutPortalProps<C>> {
121121

122122
private placeholderNode = React.createRef<HTMLDivElement>();
123+
private currentPortalNode?: PortalNode<C>;
123124

124125
constructor(props: OutPortalProps<C>) {
125126
super(props);
@@ -133,6 +134,7 @@ export class OutPortal<C extends Component<any>> extends React.PureComponent<Out
133134

134135
componentDidMount() {
135136
const node = this.props.node as PortalNode<C>;
137+
this.currentPortalNode = node;
136138

137139
const placeholder = this.placeholderNode.current!;
138140
const parent = placeholder.parentNode!;
@@ -144,6 +146,13 @@ export class OutPortal<C extends Component<any>> extends React.PureComponent<Out
144146
// We re-mount on update, just in case we were unmounted (e.g. by
145147
// a second OutPortal, which has now been removed)
146148
const node = this.props.node as PortalNode<C>;
149+
150+
// If we're switching portal nodes, we need to clean up the current one first.
151+
if (this.currentPortalNode && node !== this.currentPortalNode) {
152+
this.currentPortalNode.unmount();
153+
this.currentPortalNode = node;
154+
}
155+
147156
const placeholder = this.placeholderNode.current!;
148157
const parent = placeholder.parentNode!;
149158
node.mount(parent, placeholder);

stories/index.stories.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,45 @@ storiesOf('Portals', module)
145145
</div>
146146
});
147147
})
148+
.add('can switch between portals safely', () => {
149+
const portalNode1 = portals.createPortalNode();
150+
const portalNode2 = portals.createPortalNode();
151+
152+
const Counter = () => {
153+
const [count, setCount] = React.useState(0);
154+
155+
return <div>
156+
Count is { count }
157+
<button onClick={() => setCount(count + 1)}>
158+
+1
159+
</button>
160+
</div>;
161+
};
162+
163+
return React.createElement(() => {
164+
const [isPortalSwitched, switchPortal] = React.useState(false);
165+
166+
let portalNode = isPortalSwitched ? portalNode2 : portalNode1;
167+
168+
return <div>
169+
<portals.InPortal node={portalNode1}>
170+
<Counter />
171+
</portals.InPortal>
172+
<portals.InPortal node={portalNode2}>
173+
<Counter />
174+
</portals.InPortal>
175+
176+
<button onClick={() => switchPortal(!isPortalSwitched)}>
177+
Click to swap the portal shown
178+
</button>
179+
180+
<hr/>
181+
182+
<p>Inner OutPortal:</p>
183+
<portals.OutPortal node={portalNode} />
184+
</div>
185+
});
186+
})
148187
.add('renders reliably, even with frequent changes and multiple portals', () => {
149188
const portalNode = portals.createPortalNode('div');
150189

0 commit comments

Comments
 (0)