带有材质ui和Reactjs的嵌套侧边栏菜单

问题描述:

我正在尝试使用材质ui开发侧边栏菜单.我可以列出简单的清单.在我的项目中,我有一个无法实现的嵌套侧边栏菜单的要求.如果我尝试使用递归函数,则仅提供主标题菜单,而不呈现子元素.请帮助我开发它.

I am trying to develop a sidebar menu using material ui. I am able to make it for simple list. In my project I have a requirement of nested sidebar menu which I am not able to achieve. If I am trying to use recursive function it is providing only main title menu and not rendering child elements. Please help me develop it.

嵌套侧边栏菜单的代码如下,

The code for nested sidebar menu is as below,

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'
  },
]

首先,您有一个错字.您正在循环使用 children 键而不是 items .但是,即使您修复了该错误,您的代码仍无法按照您想要的方式工作.

First, you have a typo. You're looping on children key instead of items. But even though you fix that, your code won't still work the way you'll want.

我将通过创建可重复使用的 SingleLevel MultiLevel 可重用组件来处理当前菜单项,从而简化方法.如果当前项目具有 children / items ,那么我将使用 MultiLevel 组件,否则使用 SingleLevel .

I would simplify my approach by creating a SingleLevel and MultiLevel reusable components to handle the current menu item. If the current item has children/items then I would use the MultiLevel component else 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 菜单项.

To identify which component to use, I would then create a hasChildren helper function that returns true if the current item meets all my defined conditions to be considered as a parent menu item.

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 组件上.

I would then abstract all of these on a another MenuItem component.

MenuItem组件

const MenuItem = ({ item }) => {
  const Component = hasChildren(item) ? MultiLevel : SingleLevel;
  return <Component item={item} />;
};

最后,这就是我如何循环 menu 项目

And lastly, this is how will I loop the menu items

export default function App() {
  return menu.map((item, key) => <MenuItem key={key} item={item} />);
}