Portals


In this article, we will discuss about Portals in React.

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

Normally, when you return an element from a component’s render method, it’s mounted into the DOM as a child of the nearest parent node. 

However, sometimes it’s useful to insert a child into a different location in the DOM.

Lets understand this with an example. Lets open the index.js file from our demo-project. 

I have copied the Employee Component from our last session and Pasting it here. This Component displays the list of employees. And we are rendering this element to our root container. 

class Employee extends React.Component {

  constructor(props) {

    super(props);

    this.state = {

      employees: []

    };

  }

  

  componentDidMount() {

    fetch("https://localhost:44306/api/Employee")

      .then(res => res.json())

      .then(

        (result) => {

          this.setState({

            employees: result

          });

        }

      );

  }



  render() {

    return (

      <div>

        <h2>Employees Data...</h2>

        <table>

          <thead>

            <tr>

              <th>Id</th>

              <th>Name</th>

              <th>Location</th>

              <th>Salary</th>

            </tr>

          </thead>

          <tbody>

          {this.state.employees.map(emp => (

            <tr key={emp.Id}>

              <td>{emp.Id}</td>

              <td>{emp.Name}</td>

              <td>{emp.Location}</td>

              <td>{emp.Salary}</td>              

              </tr>

          ))}

          </tbody>

        </table>

      </div>

      );

    }

}

Lets Call this Component and render this Component to our root container. 

Now if we look at the code, we are returning one div container from the render method. We have a table inside the div. Now this div contents will be placed inside a div whose id is root. We can observe the same in the browser. Save these changes, navigate to the browser. We can see the employees data is being displayed. 

Now open developer tools, We can observe that the table is placed inside a div and this div is placed in the root div container. 

Now our requirement is that we have to place edit button in every row of our table and when we click on the edit button, we want the employee data should be displayed in a Modal Popup.  That modal popup contents may be displayed in the center of our browser. 

That means we have render an element outside of its parent’s document.

This is where we will make use of a Concept in React called as Portals. 

Now lets see how we will display a modal popup outside of our div using Portal.

Lets create one EmployeeModal Component class which will display the Employee Details in edit Mode. 

I have the code handy and I am pasting it here.

class EmployeeModal extends React.Component{

  constructor(props){

    super(props);

  }

  render(){

    return(

      <div>

        <h2>Employee Details...</h2>

        <p>

          <label>Employee ID : <input type="text" value={this.props.employee.Id}></input></label>

        </p>

        <p>

          <label>Employee Name : <input type="text" value={this.props.employee.Name}></input></label>

        </p>

        <p>

          <label>Employee Location : <input type="text" value={this.props.employee.Location}></input></label>

        </p>

        <p>

          <label>Employee Salary : <input type="text" value={this.props.employee.Salary}></input></label>

        </p>

        <input type="submit" value="Save"></input>

      </div>

    )

  }

}

We are returning a div container in which we are displaying the employee data in the textboxes. We have a save button.

Now lets add a new column to our table and with in this column we will place one edit button. 

We have display the modal pop up on click of the edit button. Lets Call a function named editEmployee on the click. 

Lets add a new property to our state object using which we will track the show and hide of our modal. 

Update this property value with in our openModal function based on its current state in our editEmployee function. That means if it is Opened now, we will close the Modal PopUp. We do the other way if not. 

Lets create one modal component. To this we will pass the children that needs to rendered, onClose function and modal popup visibility status as parameters. 

If the popus status is true, then we will display the the children of this modal pop up in a div but that div conainer should be rendered outside of the parent div. so we use a method called as ReactDOM.createPortal method and to this method we will pass the contents that needs to be displayed and the container. Lets give the DOM body as the container. 

Lets add a close button. Lets apply a class to this div using which we are setting the some css property values. I have the css class code handy and I place it in index.html file.

Now lets call this modal component from our table. To this modal we will pass open and onClose as properties. 

With in this Modal Component, we will call the EmployeeModal component and we will pass the employee data to this component class through property.

Save these changes. Navigate to the browser. Click on Edit button of any row and we can see that employee details are displayed in a Modal Popup. We are rendering the contents of the EmployeeModal Component Class outside of our root container using Portals in React. 

Whenever the contents of specific component needs to be rendered outside of the current container, like showing tooltips, or  new window that is where we will make use of Portals in React.

Remember that Even though a portal can be anywhere in the DOM tree, it behaves like a normal React child in every other way. Features like context work exactly the same regardless of whether the child is a portal, as the portal still exists in the React tree regardless of position in the DOM tree.

This includes event bubbling. An event fired from inside a portal will propagate to ancestors in the containing React tree, even if those elements are not ancestors in the DOM tree.

import ReactDOM from "react-dom";
import React, { Component } from "react";

class Employee extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      employees: [],
      showModal:false
    };
  }

  componentDidMount() {
    fetch("https://localhost:44306/api/Employee")
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            employees: result
          });
        }
      );
  }

  editEmployee=()=>{
    this.setState({showModal:!this.state.showModal});
  }
  render() {
    return (
      <div>
        <h2>Employees Data...</h2>
        <table>
          <thead>
            <tr>
              <th>Id</th>
              <th>Name</th>
              <th>Location</th>
              <th>Salary</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
          {this.state.employees.map(emp => (
            <tr key={emp.Id}>
              <td>{emp.Id}</td>
              <td>{emp.Name}</td>
              <td>{emp.Location}</td>
              <td>{emp.Salary}</td>
              <td>
                <button onClick={this.editEmployee}>Edit</button> 
                <Modal open={this.state.showModal} close={this.editEmployee}>
                  <EmployeeModal employee={emp}></EmployeeModal>
                </Modal>              
              </td>
              </tr>
          ))}
          </tbody>
        </table>
      </div>
      );
    }
}

class Modal extends React.Component{
  constructor(props){
    super(props);
  }

  render(){
    return(
      this.props.open?ReactDOM.createPortal(
        <div className="modal">
          <button onClick={this.props.close}>X</button>
          {this.props.children}
        </div>,document.body):null
      );
  }
}

class EmployeeModal extends React.Component{
  constructor(props){
    super(props);
  }
  render(){
    return(
      <div>
        <h2>Employee Details...</h2>
        <p>
          <label>Employee ID : <input type="text" value={this.props.employee.Id}></input></label>
        </p>
        <p>
          <label>Employee Name : <input type="text" value={this.props.employee.Name}></input></label>
        </p>
        <p>
          <label>Employee Location : <input type="text" value={this.props.employee.Location}></input></label>
        </p>
        <p>
          <label>Employee Salary : <input type="text" value={this.props.employee.Salary}></input></label>
        </p>
        <input type="submit" value="Save"></input>
      </div>
    )
  }
}


const element=<Employee></Employee>
ReactDOM.render(element,document.getElementById("root"));
 <style>

      .modal {

  background-color: whitesmoke;

  position: fixed;

  height: 50%;

  width: 50%;

  top: 25;

  left: 25%;

  align-items: center;

}

.modal button{

  position: absolute;

    top: 5px;

    right: 3px;

    font-size: 1rem;

    font-weight: bold;

}</style>

Video Reference:





© 2020 Pragimtech. All Rights Reserved.