How to close modal when click outside of modal

React에서 Modal창의 바깥 부분을 클릭했을 때 Modal창이 닫히게 하는 방법
Jul 26, 2023
How to close modal when click outside of modal

Modal 창을 닫는 행위가 발행하는 케이스

Modal창을 닫는 기능을 설계할 때 사용자 관점에서 생각해보면 보통 아래와 같은 경우에 닫히는 행위가 발생한다.
  1. 토글 버튼을 클릭하여 열고 닫기를 수행할 때
  1. (보통) 우측 상단의 ‘x’ 버튼이나 닫기 버튼을 클릭할 때
  1. Modal창에서의 모든 행위가 끝났을 때 (대표적으로 API 콜의 결과가 ok일 경우)
  1. Modal 창의 바깥 쪽(Outside)을 클릭할 때
이 중 마지막 4번의 경우를 React로 구현하고자 할 때 어떻게 할 수 있을까?

구현 방법

아래 코드는 재사용성을 고려하여 커스텀 훅으로 구현한 예이다.
// useOutsideClick.js import { useEffect, useRef } from "react"; export function useOutsideClick(handler, listenCapturing = true) { const ref = useRef(); useEffect( function () { function handleClick(e) { if (ref.current && !ref.current.contains(e.target)) handler(); } document.addEventListener("click", handleClick, listenCapturing); return () => document.removeEventListener("click", handleClick, listenCapturing); }, [handler, listenCapturing] ); return ref; }
Modal창 바깥쪽을 클릭했을 때 닫기 기능을 수행하는 커스텀 훅
실제 닫는 기능을 수행하는 함수를 handler라는 이름으로 전달 받아, Modal창을 제외한 바깥 영역을 클릭했을 때 닫기 기능이 동작하도록 한다. 이 때 listenCapturing이라는 이름으로 true 값을 받아 addEventListener의 3번째 인자로 전달하는 부분이 중요하다.
document.addEventListener("click", handleClick, listenCapturing);
해당 부분에 true 값을 넣어야 실제 의도한 대로 동작한다. 누락할 경우 최초 한 번은 동작하나 이후에는 동작하지 않는다. 이는 JavaScript Event handler의 일반적인 동작 방식으로 one single parent element(DOCUMENT)에 모든 event가 등록되기 때문이다.
function Window({ children, name }) { const { close, openName } = useContext(ModalContext); const ref = useOutsideClick(close); if (name !== openName) return null; return createPortal( <Overlay> <StyledModal ref={ref}> {/* 우측 상단의 닫기 아이콘 버튼 */} <Button onClick={close}> <HiXMark /> </Button> <div>{cloneElement(children, { onCloseModal: close })}</div> </StyledModal> </Overlay>, document.body ); }
커스텀 훅을 사용하는 컴포넌트
커스텀 훅의 반환 값인 ref 인자를 Modal창에 등록하여 Modal Element를 인지할 수 있도록 한다. ref={ref}
 
Share article

emoket