[React] 드롭다운 메뉴 만들기 (feat. useRef, useState)
2022. 2. 27. 15:48ㆍReact
728x90
오늘은 커스텀 훅을 사용해 드롭다운 메뉴를 만들어 보도록 하겠다.
드롭다운 메뉴의 종류가 여러 개 있지만 이 포스트에서는
그냥 평범하게 어떤 버튼을 클릭했을 때 아래와 같이 메뉴/옵션이 나오는 형식,
그리고 메뉴 밖을 클릭했을땐 메뉴가 닫히도록 설계를 해보겠다.
원리는 간단하다.
useDetectClose 라는 커스텀 훅을 만들고 현재 상태가 열린 (isOpen) 상태라면 그에 맞게 css만 적용해주면 된다.
그럼 커스텀 훅 부터 살펴보자.
hooks/useDetectCLose.js
import { useEffect, useState } from "react";
const useDetectClose = (elem, initialState) => {
const [isOpen, setIsOpen] = useState(initialState);
useEffect(() => {
const onClick = (e) => {
if (elem.current !== null && !elem.current.contains(e.target)) {
setIsOpen(!isOpen);
}
};
if (isOpen) {
window.addEventListener("click", onClick);
}
return () => {
window.removeEventListener("click", onClick);
};
}, [isOpen, elem]);
return [isOpen, setIsOpen];
};
export default useDetectClose;
일단 버튼을 클릭했을때 나오는 드롭다운 메뉴 요소를 특정할 수 있도록 ref값과 초기값을 인자로 받게 설정을 해줬다.
그리고 만약 열린 상태라면, 전역 객체에 onClick 이벤트 핸들러를 달아주어 사용자가 클릭한 요소가 메뉴 안 요소인지 확인 후,
맞다면 닫기 상태로 변경하는 로직이다.
메모리 누수 방지를 위해 mount 될 때 이벤트 핸들러를 제거해주는 것도 잊지 말자.
이제 hook을 DropDown 컴포넌트에 적용해주기만 하면 끝난다.
components/DropDown.js
import {useRef, useState} from "react";
import Link from "react-router-dom";
import styles from "styles/components/DropDown.module.scss";
import classNames from "classNames";
import useDetectClose from "hooks/useDetectClose";
const DropDown = () => {
const dropDownRef = useRef(null);
const [isOpen, setIsOpen] = useDetectClose(dropDownRef, false);
return (
<div className={styles.dropDownMenu}>
<button
onClick={() => setIsOpen(!isOpen)}
>
메뉴 보기
</button>
<ul
ref={dropDownRef}
className={classNames(styles.menu, { [styles.active]: isOpen })}
>
<li>
<Link href="/mypage">마이페이지</Link>
</li>
{/* 메뉴 리스트들 */}
</ul>
</div>
);
};
export default DropDown;
필자는 css module 방식을 사용했는데, 이건 원하는 방식으로 교체해서 사용하면 된다. 만약 열린 상태(isOpen) 일 때는 active 클래스를 달아줬다.
아래 스타일링은 참고만 하길 바란다.
.menu {
background: #fff;
border-radius: 8px;
position: absolute;
top: 30px;
right: 0;
width: 150px;
text-align: center;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.3);
opacity: 0;
visibility: hidden;
transform: translateY(-20px);
transition: opacity 0.4s ease, transform 0.4s ease, visibility 0.4s;
padding: 10px;
}
.active {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
'React' 카테고리의 다른 글
[React] 스크롤시 등장/퇴장 애니메이션 효과 적용해보기 (feat. framer motion & react-intersection-observer) (0) | 2022.09.03 |
---|---|
[React] 가로로 스크롤 진행률 표시하는 UI 만들기 (feat. 커스텀 훅) (0) | 2022.06.05 |
Quill React 에디터 사용해보기 (이미지 업로드 및 사이즈 조절) (7) | 2022.03.22 |
React 성능 최적화를 위한 useMemo 와 useCallback (0) | 2022.03.09 |
React 절대경로 설정 및 컴포넌트 불러오기 (0) | 2022.02.18 |