toggle material ui lists true/false

Viewed 48

Learning react by coding, here i have folders, which should show 'StarBorder' icon next to it when clicked, but here when i click, it shows 'StarBorder' to all folders, any advice on how to prevent it to appear on all folders ? it should appear only on that which is clicked, when ' console.log("check state", toggleSiteCheckmark)' is true then it should show, when its false then not to show and if some folder is selected and you still click it, it should hide 'StarBorder' like toggle

try it here: https://codesandbox.io/s/nestedlist-demo-material-ui-forked-pul8sk?file=/demo.tsx:0-2556

my code:

import React, { useEffect, useRef, useState } from "react";

import ListSubheader from "@mui/material/ListSubheader";
import List from "@mui/material/List";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import Collapse from "@mui/material/Collapse";
import InboxIcon from "@mui/icons-material/MoveToInbox";

import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import StarBorder from "@mui/icons-material/StarBorder";

export default function NestedList() {
  const [open, setOpen] = useState(true);

  const [toggleSiteCheckmark, setToggleSiteCheckmark] = useState(false);

  const handleClick = () => {
    setOpen(!open);
  };

  const handleSelected = () => {
    setToggleSiteCheckmark(!toggleSiteCheckmark);
  };

  console.log("check state", toggleSiteCheckmark);

  return (
    <List
      sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }}
      component="nav"
      aria-labelledby="nested-list-subheader"
      subheader={
        <ListSubheader component="div" id="nested-list-subheader">
          Nested List Items
        </ListSubheader>
      }
    >
      <ListItemButton onClick={handleClick}>
        <ListItemIcon>
          <InboxIcon />
        </ListItemIcon>
        <ListItemText primary="Folder" />
        {open ? <ExpandLess /> : <ExpandMore />}
      </ListItemButton>
      <Collapse in={open} timeout="auto" unmountOnExit>
        <List component="div" disablePadding>
          <ListItemButton onClick={handleSelected} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Inbox" />
          </ListItemButton>
          <ListItemButton onClick={handleSelected} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Sent Items" />
          </ListItemButton>
          <ListItemButton onClick={handleSelected} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Deleted Items" />
          </ListItemButton>
          <ListItemButton onClick={handleSelected} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Archieve" />
          </ListItemButton>
        </List>
      </Collapse>
    </List>
  );
}

english is not my mother language so could be mistakes.

2 Answers

The reason is you are using same condition for all the folders , to fix this issue you have to use unique values for each folder , you can use arrays for this

take a look at this codebox

or check this code

import React, { useEffect, useRef, useState } from "react";

import ListSubheader from "@mui/material/ListSubheader";
import List from "@mui/material/List";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import Collapse from "@mui/material/Collapse";
import InboxIcon from "@mui/icons-material/MoveToInbox";

import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import StarBorder from "@mui/icons-material/StarBorder";

export default function NestedList() {
  const [open, setOpen] = useState(true);

  const folders = ["Inbox", "Sent Items", "Deleted Items", "Archieve"];

  const [toggleSiteCheckmark, setToggleSiteCheckmark] = useState(0);

  const handleClick = () => {
    setOpen(!open);
  };

  const handleSelected = (id) => {
    console.log(toggleSiteCheckmark, id);
    const isReset = id === toggleSiteCheckmark ? 0 : id;
    console.log(isReset);
    setToggleSiteCheckmark(isReset);
  };

  console.log("check state", toggleSiteCheckmark);

  return (
    <List
      sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }}
      component="nav"
      aria-labelledby="nested-list-subheader"
      subheader={
        <ListSubheader component="div" id="nested-list-subheader">
          Nested List Items
        </ListSubheader>
      }
    >
      <ListItemButton onClick={handleClick}>
        <ListItemIcon>
          <InboxIcon />
        </ListItemIcon>
        <ListItemText primary="Folder" />
        {open ? <ExpandLess /> : <ExpandMore />}
      </ListItemButton>
      <Collapse in={open} timeout="auto" unmountOnExit>
        <List component="div" disablePadding>
          {folders.map((x, id) => {
            return (
              <ListItemButton
                onClick={() => handleSelected(id + 1)}
                sx={{ pl: 4 }}
              >
                <ListItemIcon>
                  {toggleSiteCheckmark === id + 1 && <StarBorder />}
                </ListItemIcon>
                <ListItemText primary="Inbox" />
              </ListItemButton>
            );
          })}

          {/* <ListItemButton onClick={handleSelected} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Sent Items" />
          </ListItemButton>
          <ListItemButton onClick={handleSelected} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Deleted Items" />
          </ListItemButton>
          <ListItemButton onClick={handleSelected} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Archieve" />
          </ListItemButton> */}
        </List>
      </Collapse>
    </List>
  );
}

You are quite close to a solution. The problem is that you are using a single boolean flag toggleSiteChecked to determine if each field is checked, but instead you need to check them uniquely. Right now if toggleSiteChecked == true, every field will act the same.

I've modified your code below to use a unique value for each item.

Edit: I also added logic to handleSelected() that unmarks a field if already marked.

import React, { useEffect, useRef, useState } from "react";

import ListSubheader from "@mui/material/ListSubheader";
import List from "@mui/material/List";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import Collapse from "@mui/material/Collapse";
import InboxIcon from "@mui/icons-material/MoveToInbox";

import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import StarBorder from "@mui/icons-material/StarBorder";

export default function NestedList() {
  const [open, setOpen] = useState(true);

  const [toggleSiteCheckmark, setToggleSiteCheckmark] = useState(null);

  const handleClick = () => {
    setOpen(!open);
  };

  const handleSelected = (value) => {
    // unmark if already marked
    if (value === toggleSiteCheckmark) {
        setToggleSiteCheckmark(null);
    } else {
        setToggleSiteCheckmark(value);
    }
  };

  console.log("check state", toggleSiteCheckmark);

  return (
    <List
      sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }}
      component="nav"
      aria-labelledby="nested-list-subheader"
      subheader={
        <ListSubheader component="div" id="nested-list-subheader">
          Nested List Items
        </ListSubheader>
      }
    >
      <ListItemButton onClick={handleClick}>
        <ListItemIcon>
          <InboxIcon />
        </ListItemIcon>
        <ListItemText primary="Folder" />
        {open ? <ExpandLess /> : <ExpandMore />}
      </ListItemButton>
      <Collapse in={open} timeout="auto" unmountOnExit>
        <List component="div" disablePadding>
          <ListItemButton onClick={() => handleSelected('Inbox')} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark === 'Inbox' && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Inbox" />
          </ListItemButton>
          <ListItemButton onClick={() => handleSelected('SentItems')} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark  === 'SentItems' && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Sent Items" />
          </ListItemButton>
          <ListItemButton onClick={() => handleSelected('DeletedItems')} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark === 'DeletedItems' && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Deleted Items" />
          </ListItemButton>
          <ListItemButton onClick={() => handleSelected('Archive')} sx={{ pl: 4 }}>
            <ListItemIcon>{toggleSiteCheckmark === 'Archive' && <StarBorder />}</ListItemIcon>
            <ListItemText primary="Archive" />
          </ListItemButton>
        </List>
      </Collapse>
    </List>
  );
}
Related