- props로 받는 onClose는 useSate의 setState이다.
- useEffect 안의 로직이 핵심이며 useRef로 감지한다.
기본 코드
import { Dispatch, SetStateAction, useEffect, useRef } from 'react';
import styled from 'styled-components';
interface LoginDropdownProps {
onClose: Dispatch<SetStateAction<boolean>>;
}
const LoginDropdown = ({ onClose }: LoginDropdownProps) => {
const selectRef = useRef<HTMLDivElement>(null);
/**
* Dropdwon 박스 바깥쪽 클릭시 닫힘
*/
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (selectRef.current && !selectRef.current.contains(event.target as Node)) {
onClose(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [onClose]);
return (
<SContainer ref={selectRef}>
<SText>사용자 로그인</SText>
<SText>관리자 로그인</SText>
</SContainer>
);
};
export default LoginDropdown;
const SContainer = styled.div`
display: flex;
flex-direction: column;
background-color: ${({ theme }) => theme.colors.gray[10]};
padding: 16px;
`;
const SText = styled.p`
cursor: pointer;
`;
문제점
원래 달성하고자 하는 목표는 2가지였다.
1. 모달 바깥을 클릭시 자동으로 닫혀야 한다.
2. 아이콘 클릭시 모달 열림, 다시 클릭시 닫힘 기능이 동작해야한다.
하지만 위와 같이 코드 작성시 아이콘을 클릭하면 검은색 모달 부분 바깥을 클릭한 것으로 인식한다.
오류 해결
그래서 useEffect 부분을 아래와 같이 수정했다.
/**
* Dropdwon 박스 바깥쪽 클릭시 닫힘
*/
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const target = event.target as Node;
if (selectRef.current && !selectRef.current.contains(target)) {
// 버튼도 포함되도록 수정 필요
const buttonEl = document.getElementById('login-button');
if (buttonEl && buttonEl.contains(target)) {
return; // 버튼 클릭은 무시
}
onClose(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [onClose]);
LoginDropdown.tsx 을 호출하는 버튼에도 id를 추가해준다.
<SActionButton id="login-button" type="button" onClick={() => setLoginIsOpen((prev) => !prev)} >
'React' 카테고리의 다른 글
[React] Vite에서 font 설정하기 (with. Styled-Components) (0) | 2025.03.05 |
---|---|
[React] 클로저 트랩 (Closure Trap, Stale Closure) 해결하기 (3) | 2025.02.13 |
[Vite] 절대경로 설정하기 (0) | 2025.01.20 |
[React] Stackflow로 모바일 웹뷰 Routing 구현하기 (1) | 2024.12.20 |
[React] input 태그의 왼쪽 0 제거하기 (0) | 2024.12.05 |