如何在数组映射中为Material-Tailwind菜单创建动态useState控制子菜单
解决Material-Tailwind多子菜单展开状态独立控制问题
当前代码中所有子菜单共用同一个menuOpen状态,导致点击任意一个菜单时,所有子菜单会同步展开/收起。要实现每个子菜单独立控制,需将状态改为记录单个菜单的展开状态,具体实现如下:
核心修改点
- 将单个布尔值状态改为对象,用菜单标题作为唯一key存储对应展开状态
- 为每个菜单单独编写展开/收起的切换逻辑
- 修正移动端状态的独立控制问题,避免移动端菜单状态冲突
修改后的完整代码
import { useState } from "react"; import { Menu, MenuHandler, MenuList, MenuItem, ListItem, Typography } from "@material-tailwind/react"; import { ChevronDownIcon } from "@heroicons/react/24/outline"; const menus = [ { title: "About", submenu: [ { subtitle: "Submenu 1-1" }, { subtitle: "Submenu 1-2" }, ], }, { title: "Contact Us", submenu: [ { subtitle: "Submenu 2-1" }, { subtitle: "Submenu 2-2" }, ], }, ]; function NavList() { // 用对象存储桌面端每个菜单的展开状态,key为菜单title const [desktopMenuStates, setDesktopMenuStates] = useState({}); // 用对象存储移动端每个菜单的展开状态 const [mobileMenuStates, setMobileMenuStates] = useState({}); // 切换桌面端指定菜单的展开状态 const toggleDesktopMenu = (menuTitle) => { setDesktopMenuStates(prev => ({ ...prev, [menuTitle]: !prev[menuTitle] })); }; // 切换移动端指定菜单的展开状态 const toggleMobileMenu = (menuTitle) => { setMobileMenuStates(prev => ({ ...prev, [menuTitle]: !prev[menuTitle] })); }; return ( <> {menus.map((menu, key) => ( <React.Fragment key={key}> {menu.submenu ? ( <> <Menu open={desktopMenuStates[menu.title] || false} handler={() => toggleDesktopMenu(menu.title)} allowHover={true} > <MenuHandler> <Typography as="div"> <ListItem selected={desktopMenuStates[menu.title] || false} onClick={() => toggleMobileMenu(menu.title)} > {menu.title} {/* 桌面端下拉图标 */} <ChevronDownIcon strokeWidth={2.5} className={`hidden h-4 w-4 transition-transform lg:block ${desktopMenuStates[menu.title] ? "rotate-180" : ""}`} /> {/* 移动端下拉图标 */} <ChevronDownIcon strokeWidth={2.5} className={`block h-3 w-3 transition-transform lg:hidden ${mobileMenuStates[menu.title] ? "rotate-180" : ""}`} /> </ListItem> </Typography> </MenuHandler> <MenuList className="hidden min-w-fit rounded-xl p-2 px-4 lg:block"> <ul className="flex flex-col gap-y-2 outline-none outline-0"> {menu.submenu.map((subItem, subKey) => ( <a href="#" key={subKey}> <MenuItem className="items-center rounded-lg p-0"> <div> <Typography variant="h6" className="font-medium text-light-blue" > {subItem.subtitle} </Typography> </div> </MenuItem> </a> ))} </ul> </MenuList> </Menu> </> ) : ( <Typography as="a" href="#"> <ListItem className="flex items-center gap-1 py-2 pr-4"> {menu.title} </ListItem> </Typography> )} </React.Fragment> ))} </> ); } export default NavList;
关键说明
- 状态隔离:通过
desktopMenuStates和mobileMenuStates两个对象,分别存储桌面端、移动端每个菜单的独立状态,避免不同菜单间的状态干扰。 - 精准切换:切换函数通过菜单标题作为唯一标识,仅更新对应菜单的状态,不会影响其他菜单。
- 变量修正:原代码中
map循环里的变量名错误(item应为menu)已统一修正,避免逻辑混淆。
内容的提问来源于stack exchange,提问作者swiftah_
相关产品推荐
相关产品推荐

