React
[React] 이중모달 구현하기
dev_seon
2022. 12. 29. 23:07
이중모달을 구현했던 과정을 정리해보겠습니다.
먼저, 제가 구현하고자 했던 이중 모달의 형태는 아래 그림과 같이 모달 형식의 글 상세 페이지 내에서 댓글을 추가하거나, 수정하거나, 삭제하기 위한 드롭다운 디자인의 이중 모달입니다.
이중 모달이 닫힌 상태에서 위쪽의 점 버튼을 누르면 이중 모달이 열리고, 이중 모달이 열린 상태에서 점 버튼을 다시 누르거나 이중 모달 외부의 영역을 클릭했을 때 이중 모달이 닫히는 기능을 구현하고자 했습니다.
구현은 점 영역 이외의 문서 전체 영역에서 click 이벤트가 발생했을 때 addEventListener로 이중 모달이 닫히는 함수를 호출하는 방식으로 진행하였고, 상세 코드는 아래와 같습니다.
// useDropDown.tsx
function useDropDown() {
const [isDrop, setIsDrop] = useState(false);
const ref = React.useRef<HTMLDivElement>(null);
// 이중 모달을 바깥쪽 클릭 시 이중 모달을 닫는 함수
// event가 발생했을 때 event의 target이 ref.current를 포함하는지 확인함
const handleClickOutside = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) {
setIsDrop(false);
}
};
// 이중 모달 open 상태를 변경하는 함수
const handleChangeDrop = (value?: boolean) => {
if (value) {
setIsDrop(value);
return;
}
setIsDrop((pre) => !pre);
};
// useEffect hook을 사용하여 화면 전체에 click 이벤트가 발생할 때 handleClickOutside 함수 실행
useEffect(() => {
document.addEventListener("click", handleClickOutside);
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, []);
return {
isDrop,
ref,
handleChangeDrop,
};
}
export default useDropDown;
// Comment.tsx
const Comment = ({ data }: CommentProps) => {
const { isDrop, ref, handleChangeDrop } = useDropDown();
return (
<CommentContainer>
<CreateInfo>
{isDrop && <CommentDropDown />} // isDrop이 true일때 이중 모달 open
<div className="info">
<ProfileImg />
<div className="author">{data.nickName}</div>
<AuthorOccupationTag occupation={data.occupation} />
<div className="created-at">{data.createdAt}</div>
</div>
// 점 영역을 ref로 설정, 점 외부의 영역 클릭 시 handleClickOutside 함수 동작
<div ref={ref}>
<Image
src={ThreeDots}
alt="edit"
onClick={() => {
handleChangeDrop(!isDrop);
}}
/>
</div>
</CreateInfo>
<p>{data.content}</p>
</CommentContainer>
);
};
다만, 위와 같이 코드를 작성하게 되면, 이중 모달을 클릭했을 때에도 이중 모달이 닫히는 문제가 발생했습니다.
때문에 console.log로 이중 모달을 클릭했을 때 동작하는 함수를 확인해보았고, 아래와 같이 이중 모달 영역을 클릭했을 때 handleClickOutside 함수가 함께 실행되는 것을 확인할 수 있었습니다.
이는 이중 모달 영역을 클릭했을 때 이중 모달의 상위 요소인 글 상세 페이지 모달 영역에도 이벤트가 전달되는 이벤트 버블링 때문임을 확인하였고, 아래와 같이 이중 모달의 Container 영역에 event.stopPropagation() 메서드를 추가하여 해결할 수 있었습니다.
const CommentDropDown = () => {
return (
<Container
onClick={(e) => {
e.stopPropagation();
}}
>
// 모달 내용 코드
</Container>
);
};
export default CommentDropDown;