krish
krish

Reputation: 77

How to Collapse only one div when using React and map

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

Answers (2)

vedanth bora
vedanth bora

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

Lenni
Lenni

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

Related Questions