我正在尝试使用材质ui开发侧边栏菜单。我可以列出简单的清单。在我的项目中,我有一个无法实现的嵌套侧边栏菜单要求。如果我尝试使用递归函数,则仅提供主标题菜单,而不呈现子元素。请帮助我开发它。
嵌套侧边栏菜单的代码如下,
import React, {useState} from 'react';
import { makeStyles } from '@material-ui/core/styles';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import Collapse from '@material-ui/core/Collapse';
import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore';
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
maxWidth: 360,
backgroundColor: theme.palette.background.paper,
},
nested: {
paddingLeft: theme.spacing(4),
},
}));
export const Menu = ({items}) => {
const classes = useStyles();
const [open, setOpen] = useState(true);
const handleClick = () => {
setOpen(!open);
};
return (
items.map(item =>
!item.children ? (
<div key={item.title}>
<ListItem button>
<ListItemIcon>
{item.icon}
</ListItemIcon>
<ListItemText primary={item.title} />
</ListItem>
</div>
) : (
<div
component="nav"
key={item.title}
>
<ListItem button onClick={handleClick}>
<ListItemIcon>
{item.icon}
</ListItemIcon>
<ListItemText primary={item.title} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItem button className={classes.nested}>
<ListItemIcon>
{item.icon}
</ListItemIcon>
<ListItemText>
<Menu items={item} />
</ListItemText>
</ListItem>
</List>
</Collapse>
</div>
)
)
);
}
菜单项代码在这里,
import HomeOutlinedIcon from "@material-ui/icons/HomeOutlined";
import LocalLibraryOutlinedIcon from "@material-ui/icons/LocalLibraryOutlined";
import TrendingUpOutlinedIcon from "@material-ui/icons/TrendingUpOutlined";
import DescriptionOutlinedIcon from "@material-ui/icons/DescriptionOutlined";
import React from "react";
export const menu = [
{
icon: <HomeOutlinedIcon/>,
title: 'Home',
items: []
},
{
icon: <LocalLibraryOutlinedIcon/>,
title: 'Education',
items: [
{
title:'Technical Analysis',
items: [
{
title: 'The Dow Theory',
to: '/thedowtheory'
},
{
title: 'Charts & Chart Patterns',
to: '/chart'
},
{
title: 'Trend & Trend Lines',
to: '/trendlines'
},
{
title: 'Support & Resistance',
to: '/sandr'
},
]
},
{
title:'Fundamental Analysis',
items: [
{
title: 'The Dow Theory',
to: '/thedowtheory'
},
{
title: 'Charts & Chart Patterns',
to: '/chart'
},
{
title: 'Trend & Trend Lines',
to: '/trendlines'
},
{
title: 'Support & Resistance',
to: '/sandr'
},
]
},
{
title:'Elliot Wave Analysis',
items: [
{
title: 'The Dow Theory',
to: '/thedowtheory'
},
{
title: 'Charts & Chart Patterns',
to: '/chart'
},
{
title: 'Trend & Trend Lines',
to: '/trendlines'
},
{
title: 'Support & Resistance',
to: '/sandr'
},
]
},
]
},
{
icon: <TrendingUpOutlinedIcon/>,
title: 'Options'
},
{
icon: <DescriptionOutlinedIcon/>,
title: 'Blog'
},
]
答案 0 :(得分:2)
首先,您有错字。您正在按children
键而不是items
循环。但是,即使您修复了该错误,您的代码仍无法按照您想要的方式工作。
我将通过创建SingleLevel
和MultiLevel
可重用组件来处理当前菜单项来简化方法。如果当前项具有children
/ items
,那么我将使用MultiLevel
组件,其他SingleLevel
。
单级组件
const SingleLevel = ({ item }) => {
return (
<ListItem button>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.title} />
</ListItem>
);
};
多级组件
const MultiLevel = ({ item }) => {
const { items: children } = item;
const [open, setOpen] = useState(false);
const handleClick = () => {
setOpen((prev) => !prev);
};
return (
<React.Fragment>
<ListItem button onClick={handleClick}>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.title} />
{open ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{children.map((child, key) => (
<MenuItem key={key} item={child} />
))}
</List>
</Collapse>
</React.Fragment>
);
};
要确定要使用哪个组件,我将创建一个hasChildren
帮助函数,如果当前项目满足我定义的所有条件都将被视为父菜单项,则返回true
。
utils.js
export function hasChildren(item) {
const { items: children } = item;
if (children === undefined) {
return false;
}
if (children.constructor !== Array) {
return false;
}
if (children.length === 0) {
return false;
}
return true;
}
然后我将所有这些抽象到另一个MenuItem
组件上。
MenuItem组件
const MenuItem = ({ item }) => {
const Component = hasChildren(item) ? MultiLevel : SingleLevel;
return <Component item={item} />;
};
最后,这就是我将如何循环menu
项
export default function App() {
return menu.map((item, key) => <MenuItem key={key} item={item} />);
}
答案 1 :(得分:1)
我已经修复了您的代码,并进行了一些重构。希望您可以使它更干净。.
import React, { useState } from "react";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import Collapse from "@material-ui/core/Collapse";
import ExpandLess from "@material-ui/icons/ExpandLess";
import ExpandMore from "@material-ui/icons/ExpandMore";
const ListItemBody = ({config}) => {
return (<>
<ListItemIcon>{config.icon}</ListItemIcon>
<ListItemText primary={config.title} />
</>);
}
const MenuItem = ({ config }) => {
return (
<ListItem button>
<ListItemBody config={config}/>
</ListItem>
);
};
const ExpandableMenuItem = ({ config }) => {
const [open, setOpen] = useState(false);
const handleClick = () => {
setOpen(!open);
};
return (
<div component="nav">
<ListItem button onClick={handleClick}>
<ListItemBody config={config}/>
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<Menu items={config.items} />
</Collapse>
</div>
);
};
export default function Menu({ items }) {
const createList = (items) => {
let menu = [];
items.map((menuItem) => {
if (Array.isArray(menuItem.items) && menuItem.items.length > 0) {
menu.push(<ExpandableMenuItem
config={menuItem}
key={menuItem.title}
/>);
} else {
menu.push(<MenuItem
config={menuItem}
key={menuItem.title}
/>);
}
});
return menu.concat();
};
return <List>{createList(items)}</List>;
}
index.js(用法)
import React from "react";
import ReactDOM from "react-dom";
import Demo from "./demo";
import HomeOutlinedIcon from "@material-ui/icons/HomeOutlined";
import LocalLibraryOutlinedIcon from "@material-ui/icons/LocalLibraryOutlined";
import TrendingUpOutlinedIcon from "@material-ui/icons/TrendingUpOutlined";
import DescriptionOutlinedIcon from "@material-ui/icons/DescriptionOutlined";
export const menu = [
{
icon: <HomeOutlinedIcon />,
title: "Home",
items: []
},
{
icon: <LocalLibraryOutlinedIcon />,
title: "Education",
items: [
{
title: "Technical Analysis",
items: [
{
title: "The Dow Theory",
to: "/thedowtheory"
},
{
title: "Charts & Chart Patterns",
to: "/chart"
},
{
title: "Trend & Trend Lines",
to: "/trendlines"
},
{
title: "Support & Resistance",
to: "/sandr"
}
]
},
{
title: "Fundamental Analysis",
items: [
{
title: "The Dow Theory1",
to: "/thedowtheory"
},
{
title: "Charts & Chart Patterns",
to: "/chart"
},
{
title: "Trend & Trend Lines",
to: "/trendlines"
},
{
title: "Support & Resistance",
to: "/sandr"
}
]
},
{
title: "Elliot Wave Analysis",
items: [
{
title: "The Dow Theory",
to: "/thedowtheory"
},
{
title: "Charts & Chart Patterns",
to: "/chart"
},
{
title: "Trend & Trend Lines",
to: "/trendlines"
},
{
title: "Support & Resistance",
to: "/sandr"
}
]
}
]
},
{
icon: <TrendingUpOutlinedIcon />,
title: "Options"
},
{
icon: <DescriptionOutlinedIcon />,
title: "Blog"
}
];
ReactDOM.render(<Demo items={menu} />, document.querySelector("#root"));