React/MUI导航栏Menu/MenuItem跳转异常,如何正确分离配置?
问题描述
开发React应用导航栏时,Movies和Actors的下拉菜单选项均跳转至同一页面(初始为/actors),尝试为两个菜单创建独立的anchorEl、open状态及处理函数后,问题反转成两个菜单都跳转至/movies页面。
问题代码
import { useState } from 'react';import { Link, useNavigate } from 'react-router-dom';import Grid from '@mui/material/Grid';import Button from '@mui/material/Button';import Menu from '@mui/material/Menu';import MenuItem from '@mui/material/MenuItem'; const Navbar = (props) => { const navigate = useNavigate(); const logout = () => { props.onAuthenticated(false); navigate('/'); }; const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); const handleClick = (event) => { setAnchorEl(event.currentTarget); }; const handleClose = () => { setAnchorEl(null); }; return ( <Grid item xs={12}> <Button component={Link} to='/'>Home</Button> <Button id="basic-button-1" aria-controls={open ? 'basic-menu-1' : undefined} aria-haspopup="true" aria-expanded={open ? 'true' : undefined} onClick={handleClick} > Movies </Button> <Button id="basic-button-2" aria-controls={open ? 'basic-menu-2' : undefined} aria-haspopup="true" aria-expanded={open ? 'true' : undefined} onClick={handleClick} > Actors </Button> <Menu id="basic-menu-1" anchorEl={anchorEl} open={open} onClose={handleClose} MenuListProps={{ 'aria-labelledby': 'basic-button-1', }} > <MenuItem component={Link} to='/movies' onClick={handleClose}>All</MenuItem> <MenuItem component={Link} to='/movies/create' onClick={handleClose}>Create</MenuItem> </Menu> <Menu id="basic-menu-2" anchorEl={anchorEl} open={open} onClose={handleClose} MenuListProps={{ 'aria-labelledby': 'basic-button-2', }} > <MenuItem component={Link} to='/actors' onClick={handleClose}>All</MenuItem> <MenuItem component={Link} to='/actors/create' onClick={handleClose}>Create</MenuItem> </Menu> {(props.authenticated) ? ( <Button variant='outlined' onClick={logout}>Logout</Button> ) : ""} </Grid> );}; export default Navbar;
问题原因
两个Menu共享同一个anchorEl和open状态,点击任意按钮时两个Menu都会被标记为open。React渲染时后一个Menu(Actors的Menu)会覆盖前一个,导致点击时实际触发的是后一个Menu的跳转链接;分开状态后反转问题,是因为处理函数与状态的对应关系错误,导致菜单激活逻辑混乱。
解决方案
新增状态跟踪当前激活的菜单ID,精确控制单个菜单的显示/隐藏:
import { useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import Grid from '@mui/material/Grid'; import Button from '@mui/material/Button'; import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; const Navbar = (props) => { const navigate = useNavigate(); const logout = () => { props.onAuthenticated(false); navigate('/'); }; // 用activeMenuId跟踪当前打开的菜单标识 const [anchorEl, setAnchorEl] = useState(null); const [activeMenuId, setActiveMenuId] = useState(null); const open = Boolean(anchorEl); const handleClick = (event, menuId) => { setAnchorEl(event.currentTarget); setActiveMenuId(menuId); }; const handleClose = () => { setAnchorEl(null); setActiveMenuId(null); }; return ( <Grid item xs={12}> <Button component={Link} to='/'>Home</Button> <Button id="basic-button-1" aria-controls={open && activeMenuId === 'movies' ? 'basic-menu-1' : undefined} aria-haspopup="true" aria-expanded={open && activeMenuId === 'movies' ? 'true' : undefined} onClick={(e) => handleClick(e, 'movies')} > Movies </Button> <Button id="basic-button-2" aria-controls={open && activeMenuId === 'actors' ? 'basic-menu-2' : undefined} aria-haspopup="true" aria-expanded={open && activeMenuId === 'actors' ? 'true' : undefined} onClick={(e) => handleClick(e, 'actors')} > Actors </Button> <Menu id="basic-menu-1" anchorEl={anchorEl} open={open && activeMenuId === 'movies'} onClose={handleClose} MenuListProps={{ 'aria-labelledby': 'basic-button-1', }} > <MenuItem component={Link} to='/movies' onClick={handleClose}>All</MenuItem> <MenuItem component={Link} to='/movies/create' onClick={handleClose}>Create</MenuItem> </Menu> <Menu id="basic-menu-2" anchorEl={anchorEl} open={open && activeMenuId === 'actors'} onClose={handleClose} MenuListProps={{ 'aria-labelledby': 'basic-button-2', }} > <MenuItem component={Link} to='/actors' onClick={handleClose}>All</MenuItem> <MenuItem component={Link} to='/actors/create' onClick={handleClose}>Create</MenuItem> </Menu> {props.authenticated && ( <Button variant='outlined' onClick={logout}>Logout</Button> )} </Grid> ); }; export default Navbar;
关键修改点
- 新增
activeMenuId状态,标记当前打开的菜单('movies'或'actors') handleClick函数新增menuId参数,点击时记录激活的菜单ID- 每个Menu的
open属性改为open && activeMenuId === 对应ID,确保仅目标菜单打开 - 调整
aria-controls和aria-expanded的判断逻辑,仅针对激活的菜单生效 - 简化Logout条件渲染,用
&&替代冗余的三元表达式
内容的提问来源于stack exchange,提问作者Marcus
相关产品推荐
相关产品推荐

