목표
원했던 구현은 해당 아이템을 클릭했을 때 모달창이 뜨고, 모달 창을 제외한 다른 곳을 클릭하면
모달 창이 사라지는 것.
문제 상황
처음 클릭 했을 때는 모달 창이 뜨지만 다른 곳을 클릭해도 모달 창이 닫히지 않는다.
모달 창의 open 과 close를 recoil boolean 상태값으로 관리했는데, 어째서인지 동작하지않는다.
// userList/index.tsx
...
const onClickHandler = (e:React.MouseEvent<HTMLDivElement>) => {
setIsUserModalOpen(true);
};
...
return (
<div css={styles.userListBox}>
<div css={styles.userListInnerBox}>
{channelUsers &&
channelUsers.map((member: ChannelUser, i: number) => {
const isOnUser = onUser.some(
(user) => user.userID === member.userId
);
const isAdmin = member.admin;
return (
// click 핸들러를 달아준 부분
<div key={i} css={styles.user} onClick={onClickHandler}>
<UserModal />
<span css={styles.isUserOnline(isOnUser, isAdmin)}></span>
<span css={styles.userName}>{member.name}</span>
<span css={styles.adminCrown(isAdmin)}>
<FontAwesomeIcon
icon={faCrown}
style={{ color: '#ffbb00' }}
/>
</span>
</div>
);
})}
</div>
</div>
//UserModal.tsx
...
const onClickHandler = (e:React.MouseEvent<HTMLDivElement>) => {
console.log(isUserModalOpen);
setIsUserModalOpen(false);
};
...
return (
<div>
<div
// 클릭하면 상태값이 false로 바뀌는 부분
onClick={onClickHandler}
css={styles.userModalBackground(isUserModalOpen)}
></div>
<div
css={styles.userModalTransition(isUserModalOpen)}
>
<div css={styles.userModalBox}>
<div css={styles.userModalRightArrow}></div>
</div>
</div>
</div>
);
};
이유가 뭘까?
처음엔 UserModal 컴포넌트의 onClickHandler를 단 background 담당 부분이 z-index에 밀려서
클릭이 안되는 줄 알고 요리조리 살펴봤지만 background는 문제가 없었다.
그래서 처음 상태값을 true로 바꿔주는 onClickHandler를 달아주는 부분을 보니
// click 핸들러를 달아준 부분
<div key={i} css={styles.user} onClick={onClickHandler}>
<UserModal />
...
UserModal 컴포넌트가 자식으로 들어가기 때문에, UserModal 컴포넌트를 클릭해도 이벤트 버블링으로
해당 e.currentTarget인 div태그의 값으로만 계속 실행되는 것 같았다.
실제로 console로 출력해보니
계속해서 true 값이 찍혔다.
컴포넌트를 부모 - 자식 관계로 두지말고 독립적으로 로직을 바꿀까도 생각해봤지만,
map을 돌려서 나온 해당 user의 바로 왼쪽에 모달을 띄우고 싶어서 css position을 적용해논 터라
유지하기로 하고, 이벤트 버블링을 막는 방법을 찾아보기로 했다.
문제 해결
stopPropagation()
이라는 메서드가 유효했다.!
해당 메서드는 부모에게서 전파되는 이벤트를 막는다.
기존 코드에 아래와 같이 적용했다.
//UserModal.tsx
const onClickHandler = (e:React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
setIsUserModalOpen(false);
};
딱 한 줄 코드를 추가함으로써 문제가 해결됐다.!
이 메서드는 앞으로 다른곳에서도 유횽하게 자주 써먹을 수 있을 것 같다.
결과