Modal 창을 닫는 행위가 발행하는 케이스
Modal창을 닫는 기능을 설계할 때 사용자 관점에서 생각해보면 보통 아래와 같은 경우에 닫히는 행위가 발생한다.
- 토글 버튼을 클릭하여 열고 닫기를 수행할 때
- (보통) 우측 상단의 ‘x’ 버튼이나 닫기 버튼을 클릭할 때
- Modal창에서의 모든 행위가 끝났을 때 (대표적으로 API 콜의 결과가 ok일 경우)
- 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;
}
실제 닫는 기능을 수행하는 함수를 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