0
\$\begingroup\$

The goal was to use React.createPortal() to render a modal component inside a <div> other than the application root. This new <div> would be removed when the modal is exited.

The Modal component is used like this:

<Modal
  open={this.state.modalOpen}
  handleClose={this.toggleModal}
  closeOnBackgroundClick
>
  <Card>
    ...etc
  </Card>
</Modal>

I'm concerned with my usage of componentWillReceiveProps to handle creation/removal of DOM nodes. It wasn't my first thought and I'm not certain if it's an appropriate use in this case. Are there downsides to my choice?

I could've used componentWillUnmount, which is useful for cleanup actions, but the way the modal is to be used doesn't lend itself to triggering the lifecycle method. In order to trigger componentWillUnmount, I need to render the component conditionally like this:

{this.state.modalOpen && (
  <Modal open={this.state.modalOpen} ...etc>
    <Card>
      ...
)}

The modal uses focus trapping and provides other W3 compliance, but I've provided a pared down version here solely for review: https://codesandbox.io/s/30wo64k5xm. The Modal.js component is the one I care about most. I will also paste the code below.

import React, { Component } from "react";
import { createPortal } from "react-dom";
import { bool, func } from "prop-types";

import styles from "./Modal.module.scss";

export default class Modal extends Component {
  static propTypes = {
    closeOnBackgroundClick: bool,
    handleClose: func.isRequired,
    open: bool.isRequired
  };

  modalRoot = document.createElement("div");

  toggleModalRoot = shouldAppend => {
    if (shouldAppend) {
      this.modalRoot.setAttribute("id", "modal-root");
      document.body.appendChild(this.modalRoot);
    } else {
      this.modalRoot.remove();
    }
  };

  componentWillReceiveProps = nextProps => {
    this.toggleModalRoot(nextProps.open);
  };

  render() {
    const { children, handleClose } = this.props;
    return createPortal(
      <div className={styles["Modal"]} onClick={handleClose} role="dialog">
        {children}
      </div>,
      this.modalRoot
    );
  }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.