Reputation: 77
I'm trying to create a dynamic menu using React and I'm struggling to implement the collapse functionality. I start by mapping each MenuItem using a Json object which works fine. Afterwards, when I tried to implement a collapse functionality when clicked on, all the menuitems collapse instead of just the one that is clicked on. I tried implementing an array which toggles between true/false but I'm having trouble accessing the value when clicked upon. Here is the code:
import React, { Component } from "react";
import styled from "styled-components";
import "./sidebar.css";
import Collapse from 'react-bootstrap/Collapse'
const dashboardItems = [
{
id: 1,
title: "Dashboard",
subtitles: ["Analysis", "Monitor", "Workpplace"],
text: "Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident."
},
{
id: 2,
title: "Form",
subtitles: ["Basic Form", "Step Form", "Advanced Form"],
text: "enim eiusmod high life accusamus terry richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident."
},
{
id: 3,
title: "List",
subtitles: ["Search Table", "Basic List", "Card List"],
text: "richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident."
},
{
id: 4,
title: "Profile",
subtitles: ["Basic Profile", "Advanced Profile"],
text: "Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident."
},
{
id: 5,
title: "Result",
subtitles: ["Success", "Failure"],
text: "craft beer labore wes anderson cred nesciunt sapiente ea proident."
},
{
id: 6,
title: "Account",
subtitles: ["Account Center", "Account Settings"],
text: "wes anderson cred nesciunt sapiente ea proident."
}
];
function Sidebar(){
const [open, setOpen] = React.useState(false);
const [clickedItems, setClickedItems] = React.useState( [false, false, false, false, false, false] );
function handleClick(id) {
console.log("initial value at " + id + " is " + clickedItems[id - 1] + " array is " + clickedItems)
clickedItems[id - 1] = !clickedItems[id - 1];
console.log(clickedItems);
setOpen(!open)
}
return (
<SidebarContainer>
<SidebarMenu>
<MenuLogo>
Dashboard
</MenuLogo>
{dashboardItems.map((postData) => {
return(
<div key = {postData.id} >
<SidebarMenuItem
onClick={() => handleClick(postData.id)}
>
<SidebarMenuItemLabel>{postData.title}</SidebarMenuItemLabel>
</SidebarMenuItem>
<Collapse in={open}>
a <div key={postData.id} id="example-collapse-text" className="collapsedText">
{postData.text}
</div>
</Collapse>
</div>
);
})}
</SidebarMenu>
</SidebarContainer>
);
}
const SidebarContainer = styled.div`
height: 100vh;
width: 270px;
background-color: #252529;
color: #fff;
display: flex;
flex-direction: column;
font-family: "Roboto", sans-serif;
`;
const SidebarMenu = styled.ul`
display: flex;
align-items: left;
flex-direction: column;
list-style: none;
width: 100%;
padding-left: 0px;
`;
const MenuLogo = styled.div`
display: flex;
align-items: center;
justify-content: start;
gap: 16px;
font-size: 18px;
line-height: 1.5;
font-weight: 600;
height: 45px;
color: #fff;
margin: 30px 30px 30px 30px;
padding-bottom: 20px;
border-bottom: 1px solid #2e2e33;
`;
const SidebarMenuItem = styled.li`
display: flex;
height: 40px;
width: 100%;
align-items: center;
padding-left: 30px;
&:hover {
background: rgba(255, 255, 255, 0.05);
box-shadow: inset 3px 0 0 0 #ffffff;
cursor: pointer;
}
`;
const SidebarMenuItemLabel = styled.p`
font-family: "Open Sans", sans-serif;
color: #fff;
font-size: 16px;
font-weight: 600;
line-height: 1.3;
text-align: left;
padding: 15px 0px;
margin-left: 15px;
margin-top: 15px;
color: #ffffff;
`;
export default Sidebar;
Please let me know if you might know of a solution. Thanks!
Upvotes: 1
Views: 6303
Reputation: 782
import React, { useState } from "react";
import List from "@mui/material/List";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Collapse from "@mui/material/Collapse";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
const getCustomOptions = () => {
const items = [
{
id: 1,
title: "Some Chart option 1",
subMenu: "some subMenu 1",
},
{
id: 2,
title: "Some Chart option 2",
subMenu: "some subMenu 2",
},
{
id: 3,
title: "Some Chart option 3",
subMenu: "some subMenu 3",
},
{
id: 4,
title: "Some Chart option 4",
subMenu: "some subMenu 4",
},
];
return items;
};
function CustomNestedList() {
const [open, setOpen] = useState({});
const items = getCustomOptions();
const handleClick = (id) => {
setOpen((prevState) => ({ ...prevState, [id]: !prevState[id] }));
};
return (
<List>
{items.map((item) => {
return (
<>
<ListItemButton onClick={() => handleClick(item.id)}>
<ListItemText primary={item.title} />
{open[item.id] ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open[item.id]} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItemButton className="pl-4">
<ListItemText primary={item.subMenu} />
</ListItemButton>
</List>
</Collapse>
</>
);
})}
</List>
);
}
export default CustomNestedList;
Upvotes: 2
Reputation: 186
Your "open" variable should be an empty object.
const [open, setOpen] = React.useState({});
Then on the handleClick function you create a new field that has "id" as key.
function handleClick(id) {
setOpen((prevState => ({...prevState, [id]: !prevState[id]}))
}
Then use that as the "open" prop.
<Collapse in={open[postData.id]}>
<div key={postData.id} id="example-collapse-text" className="collapsedText">
{postData.text}
</div>
</Collapse>
Keep in mind that when you want to access the actual state, you always want to do this:
const [state, setState] = useState(true)
setState(prevState => !prevState)
And never
const [state, setState] = useState(true)
setState(!state) // <-- THIS IS BAD
Upvotes: 1