DEV Community

Cover image for React Portals are lit πŸ”₯
Aswathprabhu R
Aswathprabhu R

Posted on

React Portals are lit πŸ”₯

Recently I was introduced to Reacts' createPortal API, which was nothing short of amazing.

Let me share my experiences with it!

Being a professional Ember developer, my love towards React has never faded away. React with its' component oriented architecture boosts productivity, ensures stable code and is backed up with strong community.

I don't wanna bore you, with this sort of things that you(probably many Web devs) hear in routine.

I think, Its' time to get our hands dirty with Portals πŸ”₯

'Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component'

Generally, not everyone in the world can understand the definition in the official docs in a single glance!, atleast NOT ME! (jus kidding, Reacts' docs on Portals is more beginner friendly peeps, go check it out)

So i decided to have a practical approach with it:

As stated in the definition, Portals provide a way to render children of a react component somewhere else in the DOM, not in the same hierarchy!

As soon i realised it, i was ended up with nothing but questions.

OMG what about the event bubbling? and many...

Being a profressional ember developer, i have used Ember Wormhole, it is an addon probably does the similar work of Portals in Ember.

I kept digging more about Portals. One thing i explored is its' use-case in Modal Dialogs.

I built a modal component with bootstrap(overriding some of bootstrap styles) in react similar to this πŸ‘‡

//Modal.js
import React from "react";
import ReactDOM from "react-dom";
export default class Modal extends React.Component {
  onClose = e => {
    this.props.onClose && this.props.onClose(e);
  };
  render() {
    let modal = (<div
      class="modal fade"
      id="exampleModalCenter"
    >
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLongTitle">
              Modal title
            </h5>
            <button
              type="button"
              class="close"
            >
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body">...</div>
          <div class="modal-footer">
            <button
              type="button"
              class="btn btn-secondary"
            >
              Close
            </button>
            <button type="button" class="btn btn-primary">
              Save changes
            </button>
          </div>
        </div>
      </div>
    </div>
    );
    return (modal);
  }
}
Enter fullscreen mode Exit fullscreen mode

I rendered it as a child to App πŸ‘‡

//App.js    
import React from "react";
import Modal from "./Modal-Compo";

export default class App extends React.Component {
  onClose = e => {
    this.props.onClose && this.props.onClose(e);
  };

  render() {
    let alignCenter = {
      display: "flex",
      alignItems: "center",
      justifyCenter: "center",
      height: "200px",
      overflow: "hidden",
      width: "50%",
      margin: "auto",
      marginTop: "10%"
    };
    return (
    <div style={alignCenter}>
      <p style={{ height: "100%", margin: "0" }}>
        //some random 100 lines
      </p>
      <Modal onClose={this.onClose}/>
    </div>
  )
 }
}


Enter fullscreen mode Exit fullscreen mode

Atlast rendered the App component in the root element πŸ‘‡

//Index.js
import React from "react";
import ReactDOM from "react-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "jquery/dist/jquery.min.js";
import "bootstrap/dist/js/bootstrap.min.js";
import App from "./components/App";
import "./styles.css";

function WhatAreModals() {
  return (
    <div style={{ height: "100vh" }} className="App">
      <App />
      <button
        type="button"
        className="btn btn-primary"
        data-toggle="modal"
        data-target="#exampleModalCenter"
      >
        Launch demo modal
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<WhatAreModals />, rootElement);

Enter fullscreen mode Exit fullscreen mode

Finally my prototype was ready πŸ˜„

When i clicked the Launch demo modal CTA, this happened (oopsy) πŸ˜•

The culprit is the App component styled 'overflow:hidden', as in our case the Modal component is rendered as a child of App whose overflow is hidden, our Modal never shows up 😩

Here is where the life saver Portal comes in πŸ”₯

I just made a tweak in my Modal component and the index.html(created another root element for Modal to be rendered)

//index.html
<div id="root"></div>
<div id="modal-root"></div>
Enter fullscreen mode Exit fullscreen mode

Rendered Modal in Portal, changed return statement by implementing createPortal

//Modal.js
ReactDOM.createPortal(modal, document.getElementById("modal-root"));
Enter fullscreen mode Exit fullscreen mode

It worked seamlessly,

The problem was solved by breaking the Modal component out of the container, out of the hierarchy.

But suddenly i got into a confusion, as the hierarchy is broken i doubted whether event bubbling will occur? (I think, Many will question this!).

I went on digging deeper 😁

Native DOM Snapshot:

React DOM:

Finally, i was satisfied seeing this, probably many would be 😌
From the snapshots, we come to know that the hierarchy is unaltered in Reacts' Virtual DOM, so event bubbling will happen with ease.

Portals can be widely used when a parent component has an overflow: hidden or z-index style, but you need the child to visually β€œbreak out” of its container. For example, dialogs, hovercards, and tooltips.

I feel this post would've satisfied you on Reacts' createPortal API, if yes, feel free to share this with your colleague web devs as well.

Top comments (2)

Collapse
 
nexxado profile image
Netanel Draiman

Very cool!
I believe it's the same concept as Angular Portal

Collapse
 
theaswathprabhu profile image
Aswathprabhu R

Absolutely πŸ˜„ πŸ‘