Skip to content
Advertisement

Closing a modal on clicking outside it with React Hooks

I’m trying to create a pop up subscription box which closes whenever one clicks on the close button or clicks anywhere outside the pop up. I’ve created a modal component and a state showModal which is used to toggle the visibility of this Modal. I have tried to add setShowModal(false) to the outer div element but that just disables the whole modal. What can be done to close the modal whenever we click outside the modal. This is how my main page looks

const [showModal, setShowModal] = useState(false);

return (
    <>
      <div
        className="homepage"
        style={{
          filter: showModal ? "blur(8px)" : "none",
          minHeight:"80vh",
        }}
      >
        <section
          className="homepage-hero"
          style={{ paddingBottom:"-2rem", minHeight:"100vh" }}
        >
          <div className="hero-body">
            <div className="container">
              <div className="columns">
                <div className="column ">
                  <h1>
                    <span className="heading">
                      Finance <br />
                      Scheme
                    </span>
                    <br />
                  </h1>
                  <p>
                   Lorem Ipsum
                  </p>
                  <div className="is-hidden-tablet">

                  </div>
                  <div className="button-group">
                    <button
                      style={{
                        fontWeight: "600",
                        padding: "0.75em 1.9em",
                        borderRadius: "0px",
                        color: "white",
                        backgroundColor: "#24ca7a",
                        border: "1px solid #24ca7a",
                        cursor: "pointer",
                      }}
                      onClick={() => setShowModal(true)}
                    >
                      Download
                    </button>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </section>
      </div>
      {showModal && (
        <Modal
          modalId="signup-modal"
          onClose={() => setShowModal(false)}
          canDismiss={false}
          modalWidth="70%"
          style={{position:"fixed", top:"20", minHeight:"50vh"}}
          >        
          <div className="contact-us" style={{  margin:"20px"}}>
            <section className="contact-form"  >
              <div className="container">
                <div className="columns is-8 ">
                  <div
                    className="column is-half-desktop is-full-mobile container-content"
                    style={{ boxShadow: "none" }}
                  >
                    {submitted ? (
                      <div className="success">
                        <img src={confirmedIllus} alt="" />
                        <h2>Your details have been submitted successfully!</h2>
                      </div>
                    ) : (
                      <form onSubmit={handleOnSubmit} id="contact-form" >
                        <h1 className="heading" style={{ fontSize: "2rem" }}>
                          Your Details
                        </h1>
                        <br />
                        <div className="field">
                          <label className="label">Name</label>
                          <div className="control">
                            <input
                              className="input"
                              id="name"
                              value={name}
                              type="text"
                              placeholder="Your Full Name"
                              onChange={(e) => setName(e.target.value)}
                              required
                            />
                          </div>
                        </div>
                        <div className="field">
                          <label className="label">Email</label>
                          <div className="control ">
                            <input
                              className={`input ${isDanger}`}
                              id="email"
                              value={email}
                              type="email"
                              onChange={handleOnChange}
                              placeholder="Your Email"
                              required
                            />
                            {!validEmail && isDanger ? (
                              <span className="icon is-small is-right">
                                <i className="material-icons-round">warning</i>
                              </span>
                            ) : (
                              " "
                            )}
                          </div>
                          {!validEmail && isDanger ? (
                            <p className="help is-danger">{emailMessage}</p>
                          ) : (
                            ""
                          )}
                        </div>

                        <div className="field is-grouped submit-button-group">
                          <div className="control">
                            <button
                              style={{
                                cursor: !validEmail ? "not-allowed" : "pointer",
                              }}
                              className="button  submit-button"
                              id="submit-form"
                            >
                              Submit
                            </button>
                          </div>
                        </div>
                      </form>
                    )}
                  </div>
                  <div className="column is-half-desktop is-full-mobile " >
                    <img
                      src="/images/Ebook.svg"
                      className="is-hidden-mobile"
                      style={{ width: "70%", marginTop: "40%" }}
                    />
                    <div className=" font-blue bottom-text">
                      Fill your details to download the free <b> Ebook </b>
                    </div>
                  </div>
                </div>
              </div>
            </section>
          </div>
        </Modal>
      )}
    </>
  );

Advertisement

Answer

You can use the useOnClickOutside hook. This hook allows you to detect clicks outside of a specified element.

You have to import the followings

Create a ref that we add to the element for which we want to detect outside clicks

const ref = useRef();

State for our modal

const [showModal, setShowModal] = useState(false);

Call hook passing in the ref and a function to call on outside click

useOnClickOutside(ref, () => setShowModal(false));

render here

return(…);

//Hook

import { useEffect } from 'react';


export default function useOnClickOutside(ref, handler) {
  useEffect(
    () => {
      const listener = (event) => {
        // Do nothing if clicking ref's element or descendent elements
        if (!ref.current || ref.current.contains(event.target)) {
          return;
        }
        handler(event);
      };
      document.addEventListener("mousedown", listener);
      document.addEventListener("touchstart", listener);
      return () => {
        document.removeEventListener("mousedown", listener);
        document.removeEventListener("touchstart", listener);
      };
    },
    // Add ref and handler to effect dependencies
    // It's worth noting that because the passed-in handler is a new ...
    // ... function on every render that will cause this effect ...
    // ... callback/cleanup to run every render. It's not a big deal ...
    // ... but to optimize you can wrap handler in useCallback before ...
    // ... passing it into this hook.
    [ref, handler]
  );
}

See related repl output here – https://spanishhotloaderprogram.thelovekesh.repl.co/

Advertisement