Header에서 마이페이지 아이콘을 클릭하면 Dropdown이 뜨고, Dropdown의 로그아웃 버튼을 통해 로그아웃할 수 있도록 기능을 만들고 싶었다. 그러나 위처럼 로그아웃 버튼이 아닌 마이페이지 아이콘만 클릭해도 로그아웃이 돼 버리는 오류를 직면하게 됐다.
export default function Header() {
const modalRef = useRef();
const [isOpen, setIsOpen] = useState(false);
const handleModal = () => {
if (isOpen === true) {
setIsOpen(false);
} else {
setIsOpen(true);
}
};
return (
<BtnContainer modal={isOpen} width="56">
<UserBtn onClick={handleModal} ref={modalRef} modal={isOpen} />
<span>마이페이지</span>
{isOpen && <Dropdown />}
</BtnContainer>
);
}
export default function Dropdown() {
const logout = removeCookie("token");
return (
<Container>
<li>마이페이지</li>
<li onClick={logout}>로그아웃</li>
</Container>
);
}
Header 컴포넌트에서 Dropdown 컴포넌트를 렌더링하고 있는 버튼은 UserBtn이다. UserBtn에서는 onClick 핸들러로 handleModal
함수를 받아 처리하고 있다.
하지만 Dropdown 컴포넌트에서는 logout
변수에서 removeCookie("token")
함수를 즉시 실행하여 결괏값을 할당하고, 이 값을 onClick 핸들러로 할당하고 있다. 이렇게 하면 Dropdown 컴포넌트가 렌더링될 때마다 removeCookie("token")
함수가 실행되고, 이로 인해 로그아웃이 수행된다. 따라서 함수 작성법을 다음과 같이 바꾸었다.
export default function Dropdown(props) {
const navigate = useNavigate();
// 수정하며 axios도 추가
const handleLogout = async () => {
try {
await AxiosInstance.post("accounts/logout/");
removeCookie("token");
removeCookie("loginType");
navigate("/");
} catch (error) {
return error.response.data;
}
};
return (
<Container>
<li>마이페이지</li>
<li onClick={handleLogout}>로그아웃</li>
</Container>
);
}
Dropdown 컴포넌트 안에서 클릭 이벤트가 발생하면 Dropdown 컴포넌트를 렌더링하는 상위 컴포넌트에서 이벤트를 처리한다. 상위 컴포넌트인 Header에서는 handleModal
함수를 UserBtn 컴포넌트의 onClick 프로퍼티로 전달하고 있다. 따라서, Dropdown 컴포넌트 안에 있는 버튼을 클릭해도 handleLogout
함수가 실행되지 않고 handleModal
함수만 호출하게 된다.
stopPropation()
을 통해 이벤트 전파를 막을 수 있을까? 하는 마음에 이런 방법도 시도해 봤다. 결과는 동일했다.
stopPropagation()
메서드를 호출하면 이벤트 버블링이 중단되기 때문에, 클릭 이벤트가 Dropdown 컴포넌트의 최상위까지 전파되지 않고 버튼에서 중단된다. 즉, Dropdown 컴포넌트의 두 번째 버튼에서 클릭 이벤트가 발생해 handleLogout
이 실행되는 것이 아니라, li
에서 이벤트가 중단되기 때문에 handleLogout
이 실행되지 않는 것이다.
const handleLogout = (e) => {
e.stopPropagation();
removeCookie("token");
};