javascript - 如何在反应中修复抽屉内的列表项
问题描述
几天前,我在这里发布了一个问题:
如何在反应中使用单一方法仅扩展更多/更少一个 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/
如您所见,定制手风琴的功能与我所需要的非常相似。如果您展开一个可折叠组,它会显示内容。如果您之后选择另一个它会消耗并显示内容,然后关闭前一个。
解决此问题的最佳解决方案是什么?
解决方案
问题是您没有存储当前展开的面板名称(或 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/
推荐阅读
- apache-spark - 使用分区模式进行 Spark 加载
- python - 来自 BeautifulSoup 结果的正则表达式不匹配模式
- java - 如何使用 Log4J 2.X 在文件中写入特定的记录器?(在 JAVA 中编程)
- java - 在 docker 中为 playwright 添加一个测试运行器
- .net - 正则表达式与数字之间的连字符和固定的整个长度
- c# - AutoFixture 中对延迟实例化的专门延迟支持
- ios - 如何在 ios 版本 14.7.1 中获得相机权限?
- java - 单个 Spring 应用程序可以在两个 kafka 集群上推送/生成消息吗?
- c++ - 如何检查文件指针是否有效?
- azure-active-directory - 是否可以使用来自现有服务主体的 Oauth 令牌连接到 Azure Databricks 中的雪花?