혼자 적어보는 노트

[React] 영역 밖 클릭시 이벤트 / 모달 닫기 본문

React

[React] 영역 밖 클릭시 이벤트 / 모달 닫기

jinist 2022. 2. 24. 05:10

 

 

영역 밖 클릭 시 이벤트 발생되는 것들을 몇차례 했었는데

이번에는 모달 컴포넌트의 바깥 영역을 클릭할 경우 해당 모달이 닫히는 것을 구현해보려한다.

 

  const modalRef = useRef();
  const [profileModal, setProfileModal] = useState(false);

  const handleCloseModal = (e) => {
    if (profileModal && !modalRef.current.contains(e.target)) {
      setProfileModal(false);
    }
  };
  useEffect(() => {
    window.addEventListener("click", handleCloseModal);
    return () => {
      window.removeEventListener("click", handleCloseModal);
    };
  }, [profileModal]);
  
  return{
    <>
      {profileModal && (
      <div ref={modalRef}>
        <ProfileModal displayName={displayName} profileImg={profileImg} />
      </div>
    )}
    </>
  }

 

발생한 문제사항들

1. ref로 함수형 컴포넌트 자체를 선택할 수 없다. (class컴포넌트는 가능)

해결방안 : div로 감싸서 해당 div에 ref를 걸어준다.

 

2. useEffect의 실행을 컴포넌트 생성 시 1번만 하게 된다면

profileModal가 true로 변경됨에도 불구하고

handleCloseModal에서 profileModal을 계속 false로 읽게된다.

해결 방안 : profileMadal의 상태가 변경할 때 마다 useEffect를 실행해 주게 변경하면 된다.

해결 방안2 : state를 관리하는 부모 컴포넌트에 useEffect를 하지 않고 모달컴포넌트에 해당 useEffect를 넣어준다.

이렇게 하면 modal컴포넌트가 활성화 되어 있을 경우에만 이벤트를 작동시킬 수 있다.

 

3. 모달을 관리하는 코드 외에 다른 코드들이 추가되어 있을 경우 코드가 복잡해보이며,

이 후 다른 곳에서도 모달을 사용할 예정인데 재사용성이 조금 떨어져보인다.

해결 방안 : 모달의 state를 관리하는 hooks를 만들어서 관리한다.

 

 


 

최종 수정

 

Header.js (부모 컴포넌트)

import ProfileModal from "./ProfileModal";
import { useModal } from "../../lib/custom/useModal";

const Header = (props) => {
  const { modalState, handleShowModal, handleCloseModal, modalRef } =
    useModal();
    
  const onClickProfile = () => {
    handleShowModal();
  };

  return (
    <header>
        <div className="profile">
            <div onClick={onClickProfile}>
              <img src={profileImg} alt="profile" />
            </div>
        </div>
        {modalState && (
          <div ref={modalRef}>
            <ProfileModal
              handleCloseModal={handleCloseModal}
            />
          </div>
        )}
    </header>
  );
};

export default Header;

 

ProfileModal.js (modal 컴포넌트)

const ProfileModal = ({ handleCloseModal }) => {
  useEffect(() => {
    window.addEventListener("mousedown", handleCloseModal);
    return () => {
      window.removeEventListener("mousedown", handleCloseModal);
    };
  }, []);

  return (
    <div className="profile-modal">
      <div className="profile-wrapper">
       
      </div>
    </div>
  );
};

export default ProfileModal;

 

useModal.js

import { useRef, useState } from "react";

export const useModal = () => {
  const [modalState, setModalState] = useState(false);

  const modalRef = useRef();

  const handleShowModal = () => {
    setModalState(!modalState);
  };
  const handleCloseModal = (e) => {
    if (modalState && !modalRef.current?.contains(e.target)) {
      setModalState(false);
    }
  };
  return {
    modalState,
    handleShowModal,
    handleCloseModal,
    modalRef,
  };
};

 

 

Comments