2뎁스 이상의 메뉴구조와 이벤트 핸들러
메뉴구조 샘플
menuData = [
{
id: "1",
useYn: "Y",
label: "menu 1",
url: "#",
children: [
{
id: "1-1",
useYn: "Y",
label: "menu 1-1",
url: "/url",
children: [],
},
{
id: "1-2",
useYn: "Y",
label: "menu 1-2",
url: "/url",
children: [],
},
]
},
{
id: "2",
useYn: "Y",
label: "menu 2",
url: "#",
children: [
{
id: "2-1",
useYn: "Y",
label: "menu 2-1",
url: "/url",
children: [],
},
],
}
]
useYn 값 으로 메뉴의 사용여부를 특정함.
children 안에 하위뎁스의 메뉴구조가 들어감.
메뉴 이벤트 핸들러와 메뉴 DOM 렌더링
openMenuId : 메뉴 item 을 특정하고 현재 메뉴와 메뉴상태를 특정하기 위한 state
navRef : 메뉴 DOM 을 특정, 메뉴 DOM 외부의 이벤트를 감지하기 위함.
handleMenuClick : click 이벤트로 openMenuId 값을 변화 시킴(토글 이벤트)
const [openMenuId, setOpenMenuId] = useState<string | null>(null);
const navRef = useRef<HTMLUListElement>(null);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (navRef.current && !navRef.current.contains(event.target as Node)) {
setOpenMenuId(null);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
}
}, []);
const handleMenuClick = (id: string) => (event: React.MouseEvent<HTMLElement>) => {
event.preventDefault();
setOpenMenuId((prev) => prev === id ? null : id);
}
return (
<ul ref={navRef}>
{menuData
.filter((item) => item.useYn === "Y")
.map((item) => (
<li key={item.id}>
<button onClick={handleMenuClick(item.id)}>{item.label}</button>
<div style={{ display: openMenuId === item.id ? "block" : "none" }}>
{item.children && (
<ul>
{item.children
?.filter((child) => child.useYn === "Y")
.map((child) => (
<li key={child.key}>
<a href={child.url}>{child.label}</a>
</li>
))
}
</ul>
)}
</div>
</li>
))
}
</ul>
);
결과
- 1 depth 메뉴 클릭 시 2 depth 메뉴 DOM 이 보여짐
- 메뉴 DOM 의 바깥을 클릭 시 메뉴상태가 초기화 됨.