티스토리 뷰

 

 

리액트에서 토글 메뉴를 구현하고, 메뉴 외부 영역을 클릭했을 때 메뉴가 닫히도록 구현하고자 했다. 

 

import React, { useState, useRef, useEffect } from "react"

export const SubMenu = () => {
    return (
        <div className="sub-menu">
            <ul className="sub-menu-list">
                <li className="sub-menu-item">
                    <button type="button" className="btn-sub-menu">메뉴 1</button>
                </li>
                <li className="sub-menu-item">
                    <button type="button" className="btn-sub-menu">메뉴 2</button>
                </li>
            </ul>
        </div>
    )
}

export const Box = () => {
    const subMenuRef = useRef<HTMLDivElement | null>(null)
    const [subMenuShow, setSubMenuShow] = useState(false);

    useEffect(() => {
        const handleClose = (e: {target: any}) => {
            if(subMenuShow && (!subMenuRef.current?.contains(e.target))) {
                setSubMenuShow(false)
            };
        }
        document.addEventListener('click', handleClose);

        return () => document.removeEventListener('click', handleClose);
    }, [subMenuShow])

    return (
        <li className="box-item">
            <div className="box-area">
                <div className="sub-menu-wrap" ref={subMenuRef}>
                    <button type="button" className="btn btn-post-manage" onClick={() => {setSubMenuShow(!subMenuShow)}}>버튼</button>
                    {subMenuShow && <SubMenu />}
                </div>
            </div>
        </li>
    )
}

 

먼저 <SubMenu>라는 토글 메뉴 컴포넌트를 만들어주었다. 

그리고 useState를 통해 상태를 만들었고, subMenuShow가 true일 때 <SubMenu>가 나타나도록 했다. 

 

useRef를 사용하여 외부영역이 아닌 버튼과 메뉴를 하나의 div에 묶어주었다.

그리고 useRef의 current에 담긴 엘리먼트가 아닌 요소가 클릭되었을 때 메뉴가 닫히도록 했다.

 

이벤트리스너를 제거해주지 않으면 컴포넌트가 리렌더링 될 때 마다 계속해서 이벤트리스너가 실행되므로, 

컴포넌트가 언마운트될 때 removeEventListener로 이벤트를 제거해서 메모리 누수가 발생하지 않도록 했다. 

 

 

참고 링크

https://babycoder05.tistory.com/entry/React-%EC%99%B8%EB%B6%80-%EC%98%81%EC%97%AD-%ED%81%B4%EB%A6%AD-%EC%8B%9C-%EB%8B%AB%EA%B8%B0

https://velog.io/@wns2252/React-%EC%99%B8%EB%B6%80-%EC%98%81%EC%97%AD-%ED%81%B4%EB%A6%AD-%EC%8B%9C-%EB%AA%A8%EB%8B%AC-%EB%8B%AB%EA%B8%B0

https://yzlosmik.tistory.com/104

 

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크