DEV Community

Rishi Raj
Rishi Raj

Posted on

Modal with a Functional Component

Yes! yet another modal tutorial but there's a catch! This one attaches an event listener on a div container rather than the document or window. Let's see how:

  1. We first create the basic Modal Functional component
import React, { useRef } from "react";

const Modal = ({ open, onClose, children }) => {
  const modalRef = useRef(null);

  return (
    open && (
      <div className="modal-overlay" ref={modalRef}>
        <div className="modal-content">{children}</div>
        <button className="close-btn" onClick={onClose}>
          Close
        </button>
      </div>
    )
  );
};

export default Modal;

Enter fullscreen mode Exit fullscreen mode
  1. We then attach our event listeners not to the document, or the window but to the div container for our Modal.
import React, { useCallback, useEffect, useRef } from "react";

const Modal = ({ open, onClose, children }) => {
  const modalRef = useRef(null);

  const handleKeyPress = useCallback(
    ({ keyCode }) => keyCode === 27 && onClose(),
    [onClose]
  );

  useEffect(() => {
    let target = modalRef && modalRef.current;
    if (target) {
      target.addEventListener("click", onClose);
      target.addEventListener("keydown", handleKeyPress);
    }

    return () => {
      if (target) {
        target.removeEventListener("click", onClose);
        target.removeEventListener("keydown", handleKeyPress);
      }
    };
  }, [modalRef, onClose, handleKeyPress]);

  return (
    open && (
      <div className="modal-overlay" ref={modalRef}>
        <div className="modal-content">{children}</div>
        <button className="close-btn" onClick={onClose}>
          Close
        </button>
      </div>
    )
  );
};

export default Modal;

Enter fullscreen mode Exit fullscreen mode
  1. We make sure to attach tabIndex="0" property on the container and focus on it as we attach the listeners. These are the two important steps to make the Modal close by pressing Èsc button.
import React, { useCallback, useEffect, useRef } from "react";

const Modal = ({ open, onClose, children }) => {
  const modalRef = useRef(null);

  const handleKeyPress = useCallback(
    ({ keyCode }) => keyCode === 27 && onClose(),
    [onClose]
  );

  useEffect(() => {
    let target = modalRef && modalRef.current;
    if (target) {
      modalRef.current.focus(); // important
      target.addEventListener("click", onClose);
      target.addEventListener("keydown", handleKeyPress);
    }

    return () => {
      if (target) {
        target.removeEventListener("click", onClose);
        target.removeEventListener("keydown", handleKeyPress);
      }
    };
  }, [modalRef, onClose, handleKeyPress]);

  // tabIndex on our div tag
  return (
    open && (
      <div tabIndex="0" className="modal-overlay" ref={modalRef}>
        <div className="modal-content">{children}</div>
        <button className="close-btn" onClick={onClose}>
          Close
        </button>
      </div>
    )
  );
};

export default Modal;

Enter fullscreen mode Exit fullscreen mode

Click here to see the Full Demo

Discussion (0)