switch from material ui lists on/off

Trying to learn React through coding, I've encountered an issue with displaying the 'StarBorder' icon next to folders when clicked. Currently, clicking on any folder displays the 'StarBorder' icon for all folders. Any tips on how to ensure that it only appears on the folder that is clicked? The icon should appear only when 'console.log("check state", toggleSiteCheckmark)' returns true. If a selected folder is clicked again, the 'StarBorder' should hide as a toggle.

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

Below is the code snippet:

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 native language, so there may be errors.

Answer №1

The issue arises from using the same condition for all folders. To resolve this, unique values must be assigned to each folder by utilizing arrays.

For a detailed example, refer to this code sandbox

Alternatively, examine the provided code snippet:

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

// List of Material-UI components
...
export default function NestedList() {
  // State variables initialization
  ...

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

  // Click event handler
  ...
}

Answer №2

You are on the right track towards finding a solution. The issue lies in using a single boolean flag toggleSiteChecked to determine if each field is checked, instead of checking them individually. With your current approach, if toggleSiteChecked == true, all fields will behave in the same way.

I have made some modifications to your code below to assign a unique value to each item.

Update: I have also included logic in handleSelected() to unmark a field if it is 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>
  );
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Displaying a box with a question mark using Glyphicon and Font Awesome icons

I'm having a problem with my website not displaying the Glyphicons or Font Awesome Icons. When I enter the code, it shows up like this: http://prntscr.com/8qwwmd. This is the first time I've encountered this issue and it's only affecting som ...

Color Swap Hover Animation

I need help implementing a color switch on hover, but I'm facing an issue where the text (within a span) is within a list item. Currently, the color changes only when hovering over the text itself, but I want it to change when hovering over the entire ...

In Angular components, data cannot be updated without refreshing the page when using setInterval()

Here's the Angular component I'm working with: export class UserListComponent implements OnInit, OnDestroy { private _subscriptions: Subscription; private _users: User[] = []; private _clickableUser: boolean = true; constructor( priv ...

What is the best way to save functions that can be utilized in both Vue front-end and Node back-end environments at the same time?

As I dive into the world of Node/Express back-end and Vue.js front-end development, along with server-side rendering, I am faced with the need to create utility functions that can format textual strings. These functions need to be accessible and reusable b ...

Enforcing the autocompletion selection in jQuery UI

As I was working on my original question, I realized that I needed to ask a separate question because I didn't fully grasp what I was trying to achieve. I am currently utilizing the jquery Tag-it plugin available at https://github.com/aehlke/tag-it a ...

Responsive Text and Alignment in the Latest Bootstrap 5

My goal is to center my div element while keeping the text aligned to the left. Here's the code I have: <div class="container"> <div class="row"> <div class="col"> <h1>< ...

I am encountering an error when trying to fetch a JSON response from a PHP script, even though I am able

Below is the Javascript code I am using to initiate an AJAX call from a PHP file: $(document).ready(function(e) { $(function(){ $.ajax({ type:'GET', dataType: 'jsonp', data: { ...

Click action: two functions, but only the first one seems to be functioning. Feeling frustrated

Here is the HTML code I am using: <td width="15%" align="center"><input name="submit" type="submit" class="button" value="Basic" tabindex="13" onclick="return submit_Click('bxbas','bxsht');" /></td> And b ...

Error: The value of 'id' cannot be assigned to an undefined property

Recently, I've been delving into learning JS Express and decided to create a basic solution to handle GET / DELETE / POST / PUT requests. Everything was running smoothly until I encountered an issue with the POST router. Below is the code snippet for ...

Upon successful authorization, the Node Express server will pass the access token to the React client app via OAuth in the callback

I am currently working on a node server that authenticates with a third party using oauth, similar to how Stack Overflow does. After authorizing the request and obtaining the access token and other essential information from the third party, my goal is to ...

Adjust the HTML layout based on the JSON data provided

I am working with a JSON script that contains live matches, which change every 5 minutes. The changes could involve keys such as live_in or score. Matches are also being deleted and added to the JSON. I want to ensure that my HTML output remains updated at ...

PHP functions triggered by ajax fail to function properly when called repeatedly

I have encountered an issue with my javascript function that calls a php script upon clicking a button or link to retrieve data. Everything works well, except when I attempt to call data from an already outputted content via the same function. Let me share ...

Unable to display individual elements of an array using the map function in React Native

Below is my react-native code that I am using to display a list of array elements using the map function. import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import {Card} from 'react-native-e ...

How can I incorporate sublists into my Note application using ReactJS?

Just starting out with ReactJS and I have a project where I need to create an app similar to Notes. Users should be able to add sublists to their notes, with each note being saved in the state as a subarray within an object. The desired state structure is ...

What is the best way to extract all ID, Name values, and locations from my JSON object and store them in an

I am working with a JSON object named 'tabledata' array. Let's say I want to iterate through all the objects inside it and extract the ID values, so the output would be 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. I also need to access other key-value pai ...

Struggling to deal with conditionals in Express

Just starting with Express and I've come across the following code: const { response } = require("express"); const express = require("express"); const app = express(); app.get("/api/products/:id", function (req, res) { ...

Passing a variable from JavaScript to PHP, utilizing the variable in an SQL query, and showcasing the results through PHP

I need to pass a JavaScript variable to a PHP script, incorporate this variable into my SQL query, and then return the results back to JavaScript. JavaScript: // Sending a variable to PHP script var variableToSend = '30'; $.post('myPHPscri ...

What is the best way to add elements to a PHP array after cross-referencing it with another array using their keys?

Array1 ( [a]=>1; [b]=>2; [c]=>3 ) Array2 ( [a]=>1;[b] =>1 ) Desired output: Array1 ( [a]=>2; [b]=>3; [c]=>3 ) Is there a way to merge the values of Array1 with Array2 based on their keys? Any help would be appreciated. Thanks. ...

Updating the state in React is only possible for a component that is currently mounted or in the

I am encountering an error message indicating that setState can only be updated on a mounted or mounting component. My intention is to toggle the display of certain content. Could you please assist me with this issue? Here is the code snippet in question ...

The Chrome browser allows interaction between two separate divs, where a button located in one div can impact

One issue I encountered involves a button located within a div that has unintended consequences for another div. Here is the basic structure of the elements: <div> <div> <div> <div> <butto ...