【问题标题】:Nested sidebar menu with material ui and Reactjs带有材质 ui 和 Reactjs 的嵌套侧边栏菜单
【发布时间】:2020-08-07 07:24:27
【问题描述】:

我正在尝试使用材质 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'
  },
]

【问题讨论】:

标签: reactjs material-ui


【解决方案1】:

首先,你有一个错字。您正在循环使用 children 键而不是 items。但即使您解决了这个问题,您的代码仍无法按照您想要的方式运行。


我将通过创建SingleLevelMultiLevel 可重用组件来处理当前菜单项来简化我的方法。如果当前项目有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 组件上。

菜单项组件

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} />);
}

【讨论】:

  • 谢谢@bertdida,这正是我想要实现的目标
  • 我们如何将 react router 与这个组件集成?
  • 尝试按照指南here 然后如果遇到问题,请发布另一个问题。 :)
【解决方案2】:

我已经修复了您的代码并进行了一些重构。我希望你能把它变得更干净..

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"));

【讨论】:

  • 我们如何将 react router 与这个组件集成?
【解决方案3】:

您可以使用material-ui-nested-menu-item。这是一个非常容易使用的 npm 库,适用于 Material UI 4。:)

很遗憾,我使用第 5 版时它不起作用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-23
    • 2017-09-20
    • 1970-01-01
    • 2016-10-06
    • 2014-07-11
    • 2014-07-03
    • 2018-12-07
    • 2012-06-22
    相关资源
    最近更新 更多