Demystifying React Portals-Don't let the parent enforce styles on the child
Sanskar Gupta
Posted on July 18, 2020
A portal is defined as a doorway, gate, or other entrance, especially an imposing and a secretive one.
This same concept is applied to a react component who is able to travel through a portal and is able to render itself on a DOM node which does NOT belong to its parent's DOM hierarchy.
Why to use it?
Every UI developer might have faced a scenario where parent css styles gets applied to the children components and this takes up huge amounts of time to come up with a workaround.
React portals enables the children not to get rendered in same DOM tree as its parent ,hence they are saved from getting css being applied to them forcefully.
Some of the use cases of portals include modals, tooltips, notification bars, toasters etc.
Example use case
Consider the following piece of code
import React from "react";
import "./styles.css";
import ReactDOM from 'react-dom'
const modalRoot = document.getElementById('root')
const modalRoot = document.getElementById('another_root')
class App extends React.Component {
state = {
show: false
};
showModal = e => {
this.setState({
show: !this.state.show
});
};
render() {
return (
<div style={{textAlign: 'center', maxWidth: '50%'}}>
<button
onClick={e => {
this.showModal(e);
}}
>
{" "}
show Modal{" "}
</button>
<Modal onClose={this.showModal} show={this.state.show}>
Text
</Modal>
</div>
);
}
}
class Modal extends React.Component {
onClose = e => {
this.props.onClose && this.props.onClose(e);
};
render() {
if (!this.props.show) {
return null;
}
return (
<div >
<h2>Modal Window</h2>
<div >{this.props.children}</div>
<div >
<button onClick={this.onClose}>
close
</button>
</div>
</div>
);
}
}
ReactDOM.render(<App />, rootElement);
Here it can be seen that the parent(App) has a div whose max width is set to 50 percent , as a result this width restriction gets applied to modal as well.
Here is the result:(Click on image to get the width idea)
Now consider this piece of code with portals:
import React from "react";
import "./styles.css";
import ReactDOM from 'react-dom'
const modalRoot = document.getElementById('root')
const modalRoot = document.getElementById('another_root')
class App extends React.Component {
state = {
show: false
};
showModal = e => {
this.setState({
show: !this.state.show
});
};
render() {
return (
<div style={{textAlign: 'center', maxWidth: '50%'}}>
<button
onClick={e => {
this.showModal(e);
}}
>
{" "}
show Modal{" "}
</button>
<Modal onClose={this.showModal} show={this.state.show}>
Text
</Modal>
</div>
);
}
}
class Modal extends React.Component {
onClose = e => {
this.props.onClose && this.props.onClose(e);
};
render() {
if (!this.props.show) {
return null;
}
return ReactDOM.createPortal (
<div >
<h2>Modal Window</h2>
<div >{this.props.children}</div>
<div >
<button onClick={this.onClose}>
close
</button>
</div>
</div>, modalRoot
);
}
}
ReactDOM.render(<App />, rootElement);
It can be seen that the modal is encompassing the whole width and not just the 50 percent as defined in parent.
ReactDOM.createPortal accepts two params , the jsx and the DOM node that is present outside like the div with id and "another_root"
It is proved that the modal is rendered under the "another_root" div and not the "root" on which the parent (App) is rendered.
The above example demonstrates the usage of a react portal considering modal as an example.
PS: It should be noted , though the child(modal) belongs to different DOM tree, there are no changes in flow of props and data i.e consider it as a normal child while dealing with props.
The above code can be found at: Link
Posted on July 18, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.