'React JS - MUI How to fix problem with expand/collapse and open drawer

I have 2 problems in the code that I can't solve.

  • The first is the opening sidebar menu. The menu opens but does not close. It should close either with a click outside or by clicking on a link. I pass the values to another file. The open state is passed correctly if it would not open. While toggleSlider is not passed saying, in the console log, undefined.
  • The second problem is the sub-menu. Without the opening code of the sub-menu, it works. I can't get it to work and it gives me error on the handleClick event (ts(2304)).

Thanks in advance for the help.

AppBar_Top.tsx:

import React, { useState } from 'react';
import { AppBar, Box, IconButton, Link, Slide, Toolbar, Typography, useScrollTrigger } from '@mui/material/';
import MenuIcon from '@mui/icons-material/Menu';
import { makeStyles } from "@mui/styles";

import Account from './appbar/Account.tsx';
import SideBar from './appbar/Sidebar.tsx';

import './style.css';


const useStyles = makeStyles((theme) => {
  return {
    root: {
      display: "flex"
    },
    appBar: {
      boxShadow: 'none',
      zIndex: 1,
      backgroundColor: '#008',
      background: 'linear-gradient(#004,#008)',
    },
    menuIcon: {
      paddingRight: 4,
      paddingLeft: 4,
      color: 'white',
    },
    menuButtonIconClosed: {
      /*transition: theme.transitions.create(
        ["transform"], {
          easing: theme.transitions.easing.sharp,
          duration: theme.transitions.duration.leavingScreen
        }
      ),*/
      transform: "rotate(0deg)",
    },
    menuButtonIconOpen: {
      /*transition: theme.transitions.create(
        ["transform"], {
          easing: theme.transitions.easing.sharp,
          duration: theme.transitions.duration.leavingScreen
        }
      ),*/
      transform: "rotate(180deg)"
    },
  }
});


// hide top bar when scrolling
interface Props {
  window?: () => Window;
  children: React.ReactElement;
}
function HideOnScroll(props: Props) {
  const { children, window } = props;
  const trigger = useScrollTrigger({
    target: window ? window() : undefined,
  });
  return (
    <Slide appear={false} direction="down" in={!trigger}>
      {children}
    </Slide>
  );
}


function AppBar_Top(props: Props) {
  //const [openModule, setOpenModule] = React.useState(false);
  
  const classes = useStyles();
  
  // open sidebar > drawer
  const [open, setOpen] = useState(false);
  const toggleSlider = () => {
    setOpen(!open);
  };

  return (
    <>
      <Box>
        <HideOnScroll {...props}>
          <AppBar id="navTop" position="fixed" color='inherit' className={classes.appBar}>
            <Toolbar>
              <Box sx={{ flexGrow: 1, display: { xs: 'flex', md: 'flex' } }}>
                <IconButton
                  edge='start'
                  className={classes.menuIcon}
                  aria-label='menu'
                  onClick={toggleSlider}
                >
                  <MenuIcon />
                </IconButton>
                <Link>
                  <img height="50px" src="./logo.svg" alt="logo" />
                </Link>
                <Typography variant="h6">
                  <div className="logoname-flex">
                    <div>
                      title
                    </div>
                    <div id="groupDescription">
                      subTitle
                    </div>
                  </div>
                </Typography>
              </Box>

              <Box sx={{ flexGrow: 0 }}>
                <Account />
              </Box>
              
              <SideBar open={open} onClose={toggleSlider} />

            </Toolbar>
          </AppBar>
        </HideOnScroll>
      </Box>
    </>
  );
}
    
export default AppBar_Top;

SideBar.tsx:

import React from 'react';
import { Avatar, Box, Collapse, Divider, Drawer, List, ListItem, ListItemButton, ListItemText, ListItemIcon } from '@mui/material/';
import { makeStyles } from "@mui/styles";


const useStyles = makeStyles((theme) => ({
  drawer: {
    top: '65px',
    background: 'linear-gradient(#004, #008)',
  },
  menuSliderContainer: {
    width: '250px',
    background: 'linear-gradient(#004, #008)',
    height: "100%",
  },
  avatar: {
    margin: "0.5rem auto",
    padding: "1rem",
    /*width: theme.spacing(13),
    height: theme.spacing(13),*/
    width: '128px',
    height: '128px',
  },
  listItem: {
    color: "tan",
  }
}));

const listItems = [
  {
    id: 1,
    title: "home",
    hidden: false,
    open: false,
    localize: "web.home",
    subMenu: null,
  },
  {
    id: 2,
    title: "groups",
    hidden: false,
    open: false,
    localize: "web.groups",
    subMenu: [
      {id: "1", name: "favorites", hidden: true, localize: "web.groups", to: "",},
      {id: "2", name: "create", hidden: false, localize: "web.create", to: "",},
      {id: "3", name: "featured", hidden: false, localize: "web.featured", to: "",},
    ]
  },
  {
    id: 3,
    title: "customize",
    hidden: false,
    open: false,
    localize: "web.customize",
    subMenu: [
      {id: "1", name: "embed", hidden: false, localize: "web.embed", to: "",},
      {id: "2", name: "events", hidden: false, localize: "web.events", to: "",},
      {id: "3", name: "customize", hidden: false, localize: "web.customize", to: "",},
    ]
  },
  {
    id: 4,
    title: "contacts",
    hidden: false,
    open: false,
    localize: "web.contacts",
    subMenu: null,
  },
  {
    id: 5,
    title: "search",
    hidden: false,
    open: false,
    localize: "web.search",
    subMenu: null,
  },
];

//({ open }) ({ toggleSlider })
export default function Sidebar({open, toggleSlider}) {
  const classes = useStyles();

  handleClick = id => {
    this.setState(state => ({
      ...state,
      settings: listItems.map(item =>
        item.id === id ? { ...item, open: !item.open } : item
      )
    }));
  };

  const menuItem = () => (
    <Box className={classes.menuSliderContainer} component="div">
      <Avatar
        className={classes.avatar}
        src="#"
        alt="abc"
      />
      <Divider />
      <List component="nav">
        {listItems.map((each, index1) => (
          (() => {
            if (each.hidden === false) {
              if (each.subMenu !== null) {
                return (
                  <React.Fragment key={index1}>
                    <ListItemButton className={classes.listItem} onClick={() => this.handleClick(each.id)}>
                      <ListItemText primary={each.title} />
                    </ListItemButton>
                    <Divider />
                    
                    <Collapse
                      in={listItems.find(item => item.id === each.id).open}
                      timeout="auto"
                      unmountOnExit
                    > 
                      <List component="div" disablePadding style={{marginLeft: "22px"}}>
                        {each.subMenu.map((subData, index2) => (
                          (() => {
                            if (subData.hidden === false) {
                              return (
                                <ListItem className={classes.listItem} key={index2} button>
                                  <ListItemText primary={subData.name} />
                                </ListItem>
                              );
                            }
                          })()
                        ))}
                      </List>
                    </Collapse>
                  </React.Fragment>
                );
              }
              else if (each.subMenu === null) {
                return (
                  <React.Fragment key={index1}>
                    <ListItemButton className={classes.listItem} >
                      <ListItemText primary={each.title} />
                    </ListItemButton>
                  </React.Fragment>
                );
              }
            }
          })()
        ))}
      </List>
    </Box>
  );

  

  console.log(`${open} ${toggleSlider}`)
  
  return (
    <>
      <Drawer classes={{paper: classes.drawer}} open={open} onClose={toggleSlider} anchor="left">
        {menuItem()}
      </Drawer>
    </>
  );
}


Solution 1:[1]

It looks as if you were destructuring your props wrongly, you wrote Sidebar({open, toggleSlider}) but on sidebar you are sending <SideBar open={open} onClose={toggleSlider} />

Sidebar({open, onClose}) should work

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Chukwuemeka Ihedoro