首页 > 解决方案 > 如何在反应中修复抽屉内的列表项

问题描述

几天前,我在这里发布了一个问题:

如何在反应中使用单一方法仅扩展更多/更少一个 ListItem

我在这里得到了一个不完整的答案,我接受它(不知道我为什么这样做,看起来它一开始就起作用了)。我不得不承认我在这里的问题可能有点不清楚。

所以,长话短说,我想修复抽屉内的列表项以正常工作。我的意思是:

这是我在这个侧边栏组件中改进的代码:

import { makeStyles } from "@material-ui/core/styles";
import Drawer from "@material-ui/core/Drawer";
import Toolbar from "@material-ui/core/Toolbar";
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";
import { Link } from "react-router-dom";
import ExpandableItem from "./expendable-item";

import HomeIcon from "@material-ui/icons/Home";
import SupervisorAccountIcon from "@material-ui/icons/SupervisorAccount";
import AccessibilityNewIcon from "@material-ui/icons/AccessibilityNew";
import FaceIcon from '@material-ui/icons/Face';
import FitnessCenterIcon from '@material-ui/icons/FitnessCenter';
import EventIcon from '@material-ui/icons/Event';
import PanoramaWideAngleIcon from '@material-ui/icons/PanoramaWideAngle';
import ReceiptIcon from '@material-ui/icons/Receipt';
import PhoneIcon from '@material-ui/icons/Phone';
import SettingsIcon from '@material-ui/icons/Settings';
import VisibilityIcon from "@material-ui/icons/Visibility";
import AddIcon from "@material-ui/icons/Add";

const drawerWidth = 280;

const useStyles = makeStyles((theme) => ({
  drawer: {
    width: drawerWidth,
    flexShrink: 0,
  },
  drawerPaper: {
    width: drawerWidth,
  },
  drawerContainer: {
    overflow: "auto",
  },
  root: {
    width: "100%",
    maxWidth: 360,
    backgroundColor: theme.palette.background.paper,
  },
  nested: {
    paddingLeft: theme.spacing(4),
  },
}));

export default function SideBar({ gymId }) {
  const classes = useStyles();

  const NonExpandableItemContainer = ({ 
    icon, 
    menuItemName, 
    menuItemLink 
  }) => (
    <ListItem 
      button 
      component={Link} 
      to={menuItemLink}
    >
      <ListItemIcon>{icon}</ListItemIcon>
      <ListItemText primary={menuItemName} />
    </ListItem>
  );

  const ExpandableItemContainer = ({
    icon,
    menuItemName,
    firstItemName,
    firstItemLink,
    secondItemName,
    secondItemLink,
  }) => (
    <ExpandableItem
      render={(xprops) => (
        <>
          <ListItem button 
            onClick={
              (menuItemName) => xprops.setOpen(!xprops.open, menuItemName)
            }
          >
            <ListItemIcon>{icon}</ListItemIcon>
            <ListItemText primary={menuItemName} />
            {xprops.open ? <ExpandLess /> : <ExpandMore />}
          </ListItem>
          <Collapse in={xprops.open} timeout="auto" unmountOnExit>
            <List component="div" disablePadding>
              <ListItem
                button
                className={classes.nested}
                component={Link}
                to={firstItemLink}
              >
                <ListItemIcon>
                  <VisibilityIcon />
                </ListItemIcon>
                <ListItemText primary={firstItemName} />
              </ListItem>
              <ListItem
                button
                className={classes.nested}
                component={Link}
                to={secondItemLink}
              >
                <ListItemIcon>
                  <AddIcon />
                </ListItemIcon>
                <ListItemText primary={secondItemName} />
              </ListItem>
            </List>
          </Collapse>
        </>
      )}
    />
  );

  return (
    <Drawer
      className={classes.drawer}
      variant="permanent"
      classes={{
        paper: classes.drawerPaper,
      }}
    >
      <Toolbar />
      <div className={classes.drawerContainer}>
        <List
          component="nav"
          aria-labelledby="nested-list-subheader"
          className={classes.root}
        >
          <NonExpandableItemContainer
            icon={<HomeIcon />}
            menuItemName="Home"
            menuItemLink={"/gym/" + gymId + "/home"}
          />
          <ExpandableItemContainer
            icon={<SupervisorAccountIcon />}
            menuItemName="Administrators"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewAccount"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addAccount"}
          />
          <ExpandableItemContainer
            icon={<AccessibilityNewIcon />}
            menuItemName="Trainers"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewAccount"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addAccount"}
          />
          <ExpandableItemContainer
            icon={<FaceIcon />}
            menuItemName="Users"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewAccount"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addAccount"}
          />
          <ExpandableItemContainer
            icon={<FitnessCenterIcon />}
            menuItemName="Trainings"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewTrainings"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addTraining"}
          />
          <NonExpandableItemContainer
            icon={<EventIcon />}
            menuItemName="Schedules"
            menuItemLink={"/gym/" + gymId + "/viewTrainingSchedules"}
          />
          <ExpandableItemContainer
            icon={<PanoramaWideAngleIcon />}
            menuItemName="Halls"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewHalls"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addHall"}
          />
          <ExpandableItemContainer
            icon={<ReceiptIcon />}
            menuItemName="Pricelist"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewPricelist"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addPricelistItem"}
          />
          <NonExpandableItemContainer
            icon={<PhoneIcon />}
            menuItemName="Contact"
            menuItemLink={"/gym/" + gymId + "/contact"}
          />
          <NonExpandableItemContainer
            icon={<SettingsIcon />}
            menuItemName="Settings"
            menuItemLink={"/gym/" + gymId + "/settings"}
          />
        </List>
      </div>
    </Drawer>
  );
}

这是我的助手可扩展项组件的代码:

import { useState } from "react";

const ExpandableItem = props => {
  const [open, setOpen] = useState(false);
  console.log(props.menuItemName);
  return props.render({ open, setOpen });
};

export default ExpandableItem;

我在 material-ui 上搜索并找到了类似的东西,但我无法合并它。这是material-ui手风琴的链接:

https://material-ui.com/components/accordion/

如您所见,定制手风琴的功能与我所需要的非常相似。如果您展开一个可折叠组,它会显示内容。如果您之后选择另一个它会消耗并显示内容,然后关闭前一个。

解决此问题的最佳解决方案是什么?

标签: javascriptreactjsmaterial-uilistitemdrawer

解决方案


问题是您没有存储当前展开的面板名称(或 ID)

import { useState } from "react";

const ExpandableItem = props => {
    const [itemState, setItemState] = useState({open: false, menuItemName: ""});

    console.log(props.menuItemName);
    return props.render({ itemState, setItemState });
};

export default ExpandableItem;

我已将变量 [open, setOpen] 重命名为 [itemState, setItemState],因此需要更改它使用的任何位置,(或者您可以添加单独的变量来设置当前选择的 menuName)

并在您的 ExpandableItemContainer 中,在 onClick 处理程序中传递适当的值 xprops.setItemState({open:!xprops.itemState.open, menuItemName}})

也在 Collapse 组件中将 in Prop 更改为

in={xprops.itemState.open && menuItemName === xprops.itemState.menuItemName}

  const ExpandableItemContainer = ({
    icon,
    menuItemName,
    firstItemName,
    firstItemLink,
    secondItemName,
    secondItemLink,
  }) => (
    <ExpandableItem
      render={(xprops) => (
        <>
          <ListItem button 
            onClick={
              (menuItemName) => xprops.setItemState({open:!xprops.itemState.open, menuItemName}})
            }
          >
            <ListItemIcon>{icon}</ListItemIcon>
            <ListItemText primary={menuItemName} />
            {xprops.open ? <ExpandLess /> : <ExpandMore />}
          </ListItem>
          <Collapse in={xprops.itemState.open} timeout="auto" unmountOnExit>
            <List component="div" disablePadding>
              <ListItem
                button
                className={classes.nested}
                component={Link}
                to={firstItemLink}
              >
                <ListItemIcon>
                  <VisibilityIcon />
                </ListItemIcon>
                <ListItemText primary={firstItemName} />
              </ListItem>
              <ListItem
                button
                className={classes.nested}
                component={Link}
                to={secondItemLink}
              >
                <ListItemIcon>
                  <AddIcon />
                </ListItemIcon>
                <ListItemText primary={secondItemName} />
              </ListItem>
            </List>
          </Collapse>
        </>
      )}
    />
  );

如果要切换选择,则需要将状态保持在顶层(列表而不是 listItem 级别,如下所示

export default function SideBar() {
const classes = useStyles();
const gymId = 1;
const [itemState, setItemState] = React.useState({
    open: false,
    menuItemName: ""
});

const NonExpandableItemContainer = ({ icon, menuItemName, menuItemLink }) => (
    <ListItem button component={Link} to={menuItemLink}>
    <ListItemIcon>{icon}</ListItemIcon>
    <ListItemText primary={menuItemName} />
    </ListItem>
);

const ExpandableItemContainer = ({
    icon,
    menuItemName,
    firstItemName,
    firstItemLink,
    secondItemName,
    secondItemLink
}) => (
    <ExpandableItem
    render={xprops => (
        <>
        <ListItem
            button
            onClick={() =>
            setItemState({
                open:
                menuItemName === itemState.menuItemName
                    ? !itemState.open
                    : true,
                menuItemName
            })
            }
        >
            <ListItemIcon>{icon}</ListItemIcon>
            <ListItemText primary={menuItemName} />
            {itemState.open ? <ExpandLess /> : <ExpandMore />}
        </ListItem>
        <Collapse
            in={itemState.open && menuItemName === itemState.menuItemName}
            timeout="auto"
            unmountOnExit
        >
            <List component="div" disablePadding>
            <ListItem
                button
                className={classes.nested}
                component={Link}
                to={firstItemLink}
            >
                <ListItemIcon>
                <VisibilityIcon />
                </ListItemIcon>
                <ListItemText primary={firstItemName} />
            </ListItem>
            <ListItem
                button
                className={classes.nested}
                component={Link}
                to={secondItemLink}
            >
                <ListItemIcon>
                <AddIcon />
                </ListItemIcon>
                <ListItemText primary={secondItemName} />
            </ListItem>
            </List>
        </Collapse>
        </>
    )}
    />
);

return (
    <Drawer
    className={classes.drawer}
    variant="permanent"
    classes={{
        paper: classes.drawerPaper
    }}
    >
    <Toolbar />
    <div className={classes.drawerContainer}>
        <List
        component="nav"
        aria-labelledby="nested-list-subheader"
        className={classes.root}
        >
        <NonExpandableItemContainer
            icon={<HomeIcon />}
            menuItemName="Home"
            menuItemLink={"/gym/" + gymId + "/home"}
        />
        <ExpandableItemContainer
            icon={<SupervisorAccountIcon />}
            menuItemName="Administrators"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewAccount"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addAccount"}
        />
        <ExpandableItemContainer
            icon={<AccessibilityNewIcon />}
            menuItemName="Trainers"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewAccount"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addAccount"}
        />
        <ExpandableItemContainer
            icon={<FaceIcon />}
            menuItemName="Members"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewAccount"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addAccount"}
        />
        <ExpandableItemContainer
            icon={<FitnessCenterIcon />}
            menuItemName="Trainings"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewTrainings"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addTraining"}
        />
        <NonExpandableItemContainer
            icon={<EventIcon />}
            menuItemName="Training schedules"
            menuItemLink={"/gym/" + gymId + "/viewTrainingSchedules"}
        />
        <ExpandableItemContainer
            icon={<PanoramaWideAngleIcon />}
            menuItemName="Halls"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewHalls"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addHall"}
        />
        <ExpandableItemContainer
            icon={<ReceiptIcon />}
            menuItemName="Pricelist"
            firstItemName="View"
            firstItemLink={"/gym/" + gymId + "/viewPricelist"}
            secondItemName="Add"
            secondItemLink={"/gym/" + gymId + "/addPricelistItem"}
        />
        <NonExpandableItemContainer
            icon={<PhoneIcon />}
            menuItemName="Contact"
            menuItemLink={"/gym/" + gymId + "/contact"}
        />
        <NonExpandableItemContainer
            icon={<SettingsIcon />}
            menuItemName="Settings"
            menuItemLink={"/gym/" + gymId + "/settings"}
        />
        </List>
    </div>
    </Drawer>
);
}

代码沙盒链接: https ://otzsl.csb.app/


推荐阅读