【React】React代码中Antd的Menu菜单使用方法
这里需要注意的是:antd的Menu菜单尽可能使用items的格式来渲染,最好不要用自己的SubMenu以及Menu.Item来做自定义渲染,否则控制台会报红色警告的 :
[antd: Menu]children
is deprecated. Please useitems
instead.
Menu的数据结构List
const MenuItems = [
{
key: 'manage',
label: '管理',
type: 'group',
},
{
key: 'management-1',
path: '/management/page', // 页面路由
label: '管理1',
icon: '' , // 设置你的icon
},
{
key: 'project',
label: '项目管理',
icon: '' , // 设置你的icon
children: [
{ key: 'project-management', path: '/project/page', label: '项目管理' },
{ key: 'create', path: '/create/page', label: '创建' },
],
},
]
Menu菜单的TS类型定义:
interface MenuItem {
key: string
label: React.ReactNode
path?: string
icon?: React.ReactNode
children?: MenuItem[]
type?: string
}
下面是完整代码
export default function Sidebar(props: { menuItems: MenuItem[] }) {
const { menuItems } = props
const location = useLocation()
const [isCollapsed, setIsCollapsed] = useState(window.innerWidth < 1024 ? true : false)
const [openKeys, setOpenKeys] = useState<string[]>([])
const [selectedKeys, setSelectedKeys] = useState<string[]>([])
const navigate = useNavigate()
const handleCollapse = useCallback(() => {
setIsCollapsed(!isCollapsed)
}, [isCollapsed])
// 监听外部窗口宽度的变化,如果小于 1024px,自动收起侧边栏
useEffect(() => {
const handleResize = () => {
if (window.innerWidth < 1024) {
setIsCollapsed(true)
} else {
setIsCollapsed(false)
}
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
useEffect(() => {
const currentKey = getKeyFromPath(location.pathname, menuItems)
const parentKey = findParentKey(location.pathname, menuItems)
// 如果找到了对应的 key,则将其设置为选中项和展开项
setSelectedKeys(currentKey ? [currentKey] : [])
setOpenKeys(parentKey ? [parentKey] : [])
}, [location.pathname, menuItems])
// 菜单的点击事件
const onMenuClick: MenuProps['onClick'] = (e) => {
const menuItem = menuItems.find((item) => item.key === e.key)
if (menuItem && menuItem.path) {
navigate(menuItem.path)
}
}
return (
<div
className={clsx(
'sidebar-menu duration-400 group relative hidden flex-col gap-1 bg-[#F8F8FC] p-2 transition-all dark:border-slate-700 dark:bg-slate-800 md:flex',
{
'w-[208px]': !isCollapsed,
'w-[70px]': isCollapsed,
},
)}
>
<Menu
theme={'light'}
onClick={onMenuClick}
defaultSelectedKeys={selectedKeys}
mode="inline"
selectedKeys={selectedKeys}
inlineCollapsed={isCollapsed}
items={menuItems.filter((item) => !isCollapsed || (isCollapsed && item.type !== 'group'))} // 过滤掉 'group' 类型的菜单项
openKeys={openKeys}
onOpenChange={(keys) => setOpenKeys(keys)}
className="mb-4 dark:bg-slate-800"
/>
<div
className={clsx('absolute bottom-0 flex h-14 cursor-pointer select-none items-center gap-2 dark:bg-gray-700 ', {
'w-[190px] px-4': !isCollapsed,
'w-[54px] justify-center px-2': isCollapsed,
})}
onClick={handleCollapse}
>
<LeftIcon
className={clsx('h-5 w-5 text-slate-400 transition-all', {
'rotate-180 transform': isCollapsed,
})}
/>
{!isCollapsed && <div className="whitespace-nowrap">收起侧边栏</div>}
</div>
</div>
)
}
其中有调用两个方法获取当前的key和上一级的key,用于处理selectedKeys和openKeys
/**
* menu菜单 - 根据当前页面路径找到对应的菜单项的 key
* @param path
* @param items
* @returns
*/
export const getKeyFromPath = (path: string, menuItems: MenuItem[]): string | null => {
const menuItem = menuItems.find((item) => item.path === path)
if (menuItem) {
return menuItem.key
}
for (const item of menuItems) {
if (item.children) {
const foundKey = getKeyFromPath(path, item.children)
if (foundKey) {
return foundKey
}
}
}
return null
}
/**
* menu菜单 - 根据path寻找上一级的key
* @param path
* @param menuItems
* @returns
*/
export const findParentKey = (path: string, menuItems: MenuItem[]): string | null => {
for (const item of menuItems) {
if ((item.children || []).some((childItem) => childItem.path === path)) {
return item.key // 找到了匹配的路径,返回父级的 key
}
}
return null // 如果未找到匹配的菜单项,返回 null
}
最后就搞定啦 ~~~