Share values and events across different components in React - reactjs

I am trying to build a dashboard app with basic AppBar and a drawer based on this Demo
https://codesandbox.io/s/nj3u0q?file=/demo.tsx
But in this Demo, AppBar and Drawer and the Main content are all in a single page.
But I made it as separate components and did the layout like this
RootContainer.tsx
import * as React from 'react';
import { styled, useTheme } from '#mui/material/styles';
import Box from '#mui/material/Box';
import CssBaseline from '#mui/material/CssBaseline';
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '#mui/material/AppBar';
import Drawer from './Drawer';
import AppBar from './AppBar';
import Main from './MainContent';
export default function PersistentDrawerLeft() {
const [open, setOpen] = React.useState(false);
return (
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<AppBar />
<Drawer />
<Main />
</Box>
);
}
Appbar.tsx
import * as React from 'react';
import { styled, useTheme } from '#mui/material/styles';
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '#mui/material/AppBar';
import Toolbar from '#mui/material/Toolbar';
import Typography from '#mui/material/Typography';
import IconButton from '#mui/material/IconButton';
import MenuIcon from '#mui/icons-material/Menu';
const drawerWidth = 240;
interface AppBarProps extends MuiAppBarProps {
open?: boolean;
}
const AppBar = styled(MuiAppBar, {
shouldForwardProp: (prop) => prop !== 'open',
})<AppBarProps>(({ theme, open }) => ({
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(open && {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: `${drawerWidth}px`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
}),
}));
export default function AppBar() {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
return (
<AppBar position="fixed" style={{background:'#002a5e'}} open={open}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
sx={{ mr: 2, ...(open && { display: 'none' }) }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
Test
</Typography>
</Toolbar>
</AppBar>
);
}
But the problem is when I click the sandwich button on the appbar, it reduces its width to show the Drawer, but the drawer not at all showing.
Drawer.tsx
import * as React from "react";
import { styled, useTheme } from "#mui/material/styles";
import Drawer from "#mui/material/Drawer";
import List from "#mui/material/List";
import Divider from "#mui/material/Divider";
import IconButton from "#mui/material/IconButton";
import ChevronLeftIcon from "#mui/icons-material/ChevronLeft";
import ChevronRightIcon from "#mui/icons-material/ChevronRight";
import ListItem from "#mui/material/ListItem";
import ListItemButton from "#mui/material/ListItemButton";
import ListItemIcon from "#mui/material/ListItemIcon";
import ListItemText from "#mui/material/ListItemText";
import InboxIcon from "#mui/icons-material/MoveToInbox";
import MailIcon from "#mui/icons-material/Mail";
const drawerWidth = 240;
const DrawerHeader = styled("div")(({ theme }) => ({
display: "flex",
alignItems: "center",
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
justifyContent: "flex-end",
}));
export default function Drawer() {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: drawerWidth,
boxSizing: "border-box",
},
}}
variant="persistent"
anchor="left"
open={open}
>
<DrawerHeader>
<IconButton onClick={handleDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</DrawerHeader>
<Divider />
<List>
{["Manage Recipe", "Reports", "Settings"].map((text, index) => (
<ListItem key={text} disablePadding>
<ListItemButton>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItemButton>
</ListItem>
))}
</List>
</Drawer>
);
}
I guess this problem is because the open constant is not getting updated into the drawer component. I am pretty new or only fews days of experience in react. Please help how can share these click event and constant in appbar to the drawer

It looks like with the separation, each component is having their own open state and handling functions, therefore the behavior is not shared.
Try pass these as props from the parent to communicate with children components.
React document about: Passing Props to a Component
There seems to be already a open state in the parent component RootContainer, so perhaps this state and handlers can be passed as props to the components, such as below example.
Over simplified example from forked demo: codesandbox
Define state and handler in the parent component, and pass these down to the children:
RootContainer.tsx
export default function PersistentDrawerLeft() {
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<Box sx={{ display: "flex" }}>
<CssBaseline />
<AppBar open={open} onDrawerOpen={handleDrawerOpen} />
<Drawer open={open} onDrawerClose={handleDrawerClose} />
<Main />
</Box>
);
}
Have each child component use the open state and handlers from the props, instead of their own, so that they share the same state:
Appbar.tsx
export default function AppBar({ open, onDrawerOpen }) {
const theme = useTheme();
return (
<AppBar position="fixed" style={{ background: "#002a5e" }} open={open}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={onDrawerOpen}
edge="start"
sx={{ mr: 2, ...(open && { display: "none" }) }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
Test
</Typography>
</Toolbar>
</AppBar>
);
}
Drawer.tsx
export default function Drawer({ open, onDrawerClose }) {
const theme = useTheme();
return (
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: drawerWidth,
boxSizing: "border-box"
}
}}
variant="persistent"
anchor="left"
open={open}
>
<DrawerHeader>
<IconButton onClick={onDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</DrawerHeader>
<Divider />
<List>
{["Manage Recipe", "Reports", "Settings"].map((text, index) => (
<ListItem key={text} disablePadding>
<ListItemButton>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItemButton>
</ListItem>
))}
</List>
</Drawer>
);
}
Hope this will help.

Related

How to make Material UI Temporary Drawer button as icon instead of text?

I'm using Material UI to create a navigation bar with Temporary Drawer. When the user clicks on the hamburger menu icon, I want the menu to fade-in to the screen and slide from the right.
Basically, all the functionality works, except only the button named 'RIGHT' works instead of the hamburger icon I have created beside it..
I have tried removing 'right' and replace it with the icon, but when I do that the menu comes out from left-top...
import React, { useEffect, useState } from 'react'
import { makeStyles } from '#material-ui/core/styles';
import { AppBar, IconButton, Toolbar, Typography, Collapse } from '#material-ui/core';
import SortIcon from '#material-ui/icons/Sort';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import { Link as Scroll } from 'react-scroll'
import clsx from 'clsx';
import Drawer from '#material-ui/core/Drawer';
import Button from '#material-ui/core/Button';
import List from '#material-ui/core/List';
import Divider from '#material-ui/core/Divider';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import InboxIcon from '#material-ui/icons/MoveToInbox';
import MailIcon from '#material-ui/icons/Mail';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: "100vh",
fontFamily: "Roboto",
},
appbar: {
background: "none",
},
appbarWrapper: {
width: "80%",
margin: "0 auto"
},
appbarTitle: {
fontSize: "2rem",
flexGrow: '1',
},
icon: {
color: '#fff',
fontSize: "2rem",
},
colorText: {
color: "#5AFF3D",
},
container: {
textAlign: "center",
},
title: {
color: "#fff",
fontSize: "4.5rem",
},
goDown: {
color: '#5AFF3D',
fontSize: '4rem',
},
list: { //NEW
width: 250,
},
fullList: {
width: 'auto',
},
}));
export default function Header() {
const classes = useStyles();
const [checked, setChecked] = useState(false);
useEffect(() => {
setChecked(true);
}, [])
// NEW
const [state, setState] = React.useState({
right: false,
});
const toggleDrawer = (anchor, open) => (event) => {
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
return;
}
setState({ ...state, [anchor]: open });
};
const list = (anchor) => (
<div
className={clsx(classes.list, {
[classes.fullList]: anchor === 'top' || anchor === 'bottom',
})}
role="presentation"
onClick={toggleDrawer(anchor, false)}
onKeyDown={toggleDrawer(anchor, false)}
>
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{['All mail', 'Trash', 'Spam'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</div>
);
/*
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
*/
return (
<div className={classes.root} id="header">
<AppBar className={classes.appbar} elevation={0}>
<Toolbar className={classes.appbarWrapper}>
<h1 className={classes.appbarTitle}>
We<span className={classes.colorText}>cycle</span>
</h1>
<IconButton>
<SortIcon className={classes.icon} /*onClick={handleClick} aria-control="fade-menu" aria-haspopup="true"*/ />
{['right'].map((anchor) => (
<React.Fragment key={anchor}>
<Button onClick={toggleDrawer(anchor, true)}>{anchor}</Button>
<Drawer anchor={anchor} open={state[anchor]} onClose={toggleDrawer(anchor, false)}>
{list(anchor)}
</Drawer>
</React.Fragment>
))}
</IconButton>
{/* <Menu
id="fade-menu"
anchorEl={anchorEl}
keepMounted
open={open}
onClose={handleClose}
TransitionComponent={Fade}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu> */}
</Toolbar>
</AppBar>
<Collapse
in={checked} {...(checked ? { timeout: 1000 } : {})}
collapsedHeight={50}
>
<div className={classes.container}>
<h1 className={classes.title}>
Meet the <br /> <span className={classes.colorText}>Team </span>
</h1>
<Scroll to="place-to-visit" smooth={true}>
<IconButton>
<ExpandMoreIcon className={classes.goDown} />
</IconButton>
</Scroll>
</div>
</Collapse>
</div>
);
}
I've made your code work for what you need and left some comments inside. I don't know what you ideally want to make, but don't copy all the code from the MUI example which you might not fully understand.
const [anchorEl, setAnchorEl] = useState(false);
// const open = Boolean(anchorEl);
const handleDrawerOpen = () => {
setAnchorEl(true);
};
const handleDrawerClose = () => {
setAnchorEl(false);
};
Above is used to control the state of Drawer.
Following is the code that you can just copy.
import React, { useEffect, useState } from 'react'
import { makeStyles } from '#material-ui/core/styles';
import { AppBar, IconButton, Toolbar, Typography, Collapse } from '#material-ui/core';
import SortIcon from '#material-ui/icons/Sort';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import { Link as Scroll } from 'react-scroll'
import clsx from 'clsx';
import Drawer from '#material-ui/core/Drawer';
import Button from '#material-ui/core/Button';
import List from '#material-ui/core/List';
import Divider from '#material-ui/core/Divider';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import InboxIcon from '#material-ui/icons/MoveToInbox';
import MailIcon from '#material-ui/icons/Mail';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: "100vh",
fontFamily: "Roboto",
},
appbar: {
background: "none",
},
appbarWrapper: {
width: "80%",
margin: "0 auto"
},
appbarTitle: {
fontSize: "2rem",
flexGrow: '1',
},
icon: {
color: '#fff',
fontSize: "2rem",
},
colorText: {
color: "#5AFF3D",
},
container: {
textAlign: "center",
},
title: {
color: "#fff",
fontSize: "4.5rem",
},
goDown: {
color: '#5AFF3D',
fontSize: '4rem',
},
list: { //NEW
width: 250,
},
fullList: {
width: 'auto',
},
}));
export default function Header() {
const classes = useStyles();
const [checked, setChecked] = useState(false);
useEffect(() => {
setChecked(true);
}, [])
// NEW
const [state, setState] = React.useState({
right: false,
});
const toggleDrawer = (anchor, open) => (event) => {
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
return;
}
setState({ ...state, [anchor]: open });
};
const list = (anchor) => (
<div
className={clsx(classes.list, {
[classes.fullList]: anchor === 'top' || anchor === 'bottom',
})}
role="presentation"
onClick={toggleDrawer(anchor, false)}
onKeyDown={toggleDrawer(anchor, false)}
>
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{['All mail', 'Trash', 'Spam'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</div>
);
const [anchorEl, setAnchorEl] = useState(false);
// const open = Boolean(anchorEl);
const handleDrawerOpen = () => {
setAnchorEl(true);
};
const handleDrawerClose = () => {
setAnchorEl(false);
};
return (
<div className={classes.root} id="header">
<AppBar className={classes.appbar} elevation={0}>
<Toolbar className={classes.appbarWrapper}>
<h1 className={classes.appbarTitle}>
We<span className={classes.colorText}>cycle</span>
</h1>
{/* You Don't need the map here unless you want many button to toggle one Drawer,
like the example on https://material-ui.com/components/drawers/.
and the point here for your question is to move the onClick to IconButton, and you may
not want the button inside IconButton, since it is a button already,
the inside button would make it a little ugly */}
<IconButton onClick={handleDrawerOpen} onClose={handleDrawerClose}>
<SortIcon className={classes.icon} /*onClick={handleClick} aria-control="fade-menu" aria-haspopup="true"*/ />
RIGHT{/* delete the text here if you don't need */}
</IconButton>
<Drawer anchor='right' open={anchorEl} onClose={handleDrawerClose}>
{list('right')}
</Drawer>
{/* <Menu
id="fade-menu"
anchorEl={anchorEl}
keepMounted
open={open}
onClose={handleClose}
TransitionComponent={Fade}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu> */}
</Toolbar>
</AppBar>
<Collapse
in={checked} {...(checked ? { timeout: 1000 } : {})}
collapsedHeight={50}
>
<div className={classes.container}>
<h1 className={classes.title}>
Meet the <br /> <span className={classes.colorText}>Team </span>
</h1>
<Scroll to="place-to-visit" smooth={true}>
<IconButton>
<ExpandMoreIcon className={classes.goDown} />
</IconButton>
</Scroll>
</div>
</Collapse>
</div>
);
}
Recomandation: since you have import React, { useEffect, useState } from 'react', you may not use React.useState() but use useState() directly.

converting material ui functional component Mini variant drawer to class component Reactjs

I am using mini variant drawer from material-ui official website
https://material-ui.com/components/drawers/#drawer
I am trying to convert into class component, but alot of issues, craches comes up. Some of them are hooks related and occurs through node modules.
Can any body have used this component in class component
import React from 'react'
import clsx from 'clsx'
import {
createStyles,
makeStyles,
useTheme,
Theme
} from '#material-ui/core/styles'
import Drawer from '#material-ui/core/Drawer'
import AppBar from '#material-ui/core/AppBar'
import Toolbar from '#material-ui/core/Toolbar'
import List from '#material-ui/core/List'
import CssBaseline from '#material-ui/core/CssBaseline'
import Typography from '#material-ui/core/Typography'
import Divider from '#material-ui/core/Divider'
import IconButton from '#material-ui/core/IconButton'
import MenuIcon from '#material-ui/icons/Menu'
import ChevronLeftIcon from '#material-ui/icons/ChevronLeft'
import ChevronRightIcon from '#material-ui/icons/ChevronRight'
import ListItem from '#material-ui/core/ListItem'
import ListItemIcon from '#material-ui/core/ListItemIcon'
import ListItemText from '#material-ui/core/ListItemText'
import InboxIcon from '#material-ui/icons/MoveToInbox'
import MailIcon from '#material-ui/icons/Mail'
const drawerWidth = 240
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
display: 'flex'
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
backgroundColor: 'transparent',
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
appBarShift: {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen
})
},
menuButton: {
marginRight: 36
},
hide: {
display: 'none'
},
drawer: {
width: drawerWidth,
flexShrink: 0,
whiteSpace: 'nowrap'
},
drawerOpen: {
width: drawerWidth,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen
})
},
drawerClose: {
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
overflowX: 'hidden',
width: theme.spacing(7) + 1,
[theme.breakpoints.up('sm')]: {
width: theme.spacing(9) + 1
}
},
toolbar: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar
},
content: {
flexGrow: 1,
padding: theme.spacing(3)
}
})
)
export default function MiniDrawer () {
const classes = useStyles()
const theme = useTheme()
const [open, setOpen] = React.useState(true)
const handleDrawerOpen = () => {
setOpen(true)
}
const handleDrawerClose = () => {
console.log('close clicked =')
setOpen(false)
console.log('open =', open)
}
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position='fixed'
className={clsx(classes.appBar, {
[classes.appBarShift]: open
})}
>
<Toolbar>
<IconButton
color='inherit'
aria-label='open drawer'
onClick={handleDrawerOpen}
edge='start'
className={clsx(classes.menuButton, {
[classes.hide]: open
})}
>
<MenuIcon />
</IconButton>
<Typography variant='h6' noWrap>
Mini variant drawer
</Typography>
</Toolbar>
</AppBar>
<Drawer
variant='permanent'
className={clsx(classes.drawer, {
[classes.drawerOpen]: open,
[classes.drawerClose]: !open
})}
classes={{
paper: clsx({
[classes.drawerOpen]: open,
[classes.drawerClose]: !open
})
}}
>
<div className={classes.toolbar}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'rtl' ? (
<ChevronRightIcon />
) : (
<ChevronLeftIcon />
)}
</IconButton>
</div>
<Divider />
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{['All mail', 'Trash', 'Spam'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
<main className={classes.content}>
<div className={classes.toolbar} />
<Typography paragraph></Typography>
<Typography paragraph></Typography>
</main>
</div>
)
}
You cannot use hooks in a class component. Create a state property in your class and use it instead.
From react docs: Converting a Function to a Class
You can convert a function component like Clock to a class in five steps:
Create an ES6 class, with the same name, that extends React.Component.
Add a single empty method to it called render().
Move the body of the function into the render() method.
Replace props with this.props in the render() body.
Delete the remaining empty function declaration.
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

Conditional Rendering in Nextjs & TypeScript not working

Trying to render a component conditionally. I have a drawHelper variable & a function to toggle it true or false. The component renders or not based on the initial value of drawHelper. (false, doesn't render, true it does).
The toggle function changes the value. I checked with console.log(drawHelper) But changing the value does not make the component appear or disappear.
Am I missing something here?
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import CssBaseline from '#material-ui/core/CssBaseline';
import Divider from '#material-ui/core/Divider';
import Drawer from '#material-ui/core/Drawer';
import Hidden from '#material-ui/core/Hidden';
import IconButton from '#material-ui/core/IconButton';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import DashboardIcon from '#material-ui/icons/Dashboard';
import MenuIcon from '#material-ui/icons/Menu';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { makeStyles, useTheme, Theme, createStyles } from '#material-ui/core/styles';
import { User } from './User';
import { Draw } from "components/Layout/Countryballs/Draw";
const drawerWidth = 240;
export const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
display: 'flex',
color: '#fff',
},
drawer: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0,
},
},
appBar: {
marginLeft: drawerWidth,
[theme.breakpoints.up('sm')]: {
width: `calc(100% - ${drawerWidth}px)`,
},
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up('sm')]: {
display: 'none',
},
},
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
backgroundColor: theme.palette.primary.main
},
content: {
flexGrow: 1,
},
menuItem: {
color: '#fff',
},
}),
);
export const Layout: React.FC<LayoutProps> = (props) => {
const { container } = props;
const classes = useStyles();
const theme = useTheme();
const [mobileOpen, setMobileOpen] = React.useState(false);
function handleDrawerToggle() {
setMobileOpen(!mobileOpen);
}
// Display Draw component
// 1 Create property
var drawHelper: Boolean = false;
function toggleDraw() {
console.log(drawHelper);
drawHelper = !drawHelper;
}
const drawer = (
<div>
<div className={classes.toolbar} />
<Divider />
<List>
{['Draw'].map((text) => (
<ListItem button key={text} onClick={toggleDraw} className={classes.menuItem}>
<ListItemIcon className={classes.menuItem}><DashboardIcon /></ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</div>
);
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
Project name
</Typography>
<User/>
</Toolbar>
</AppBar>
<nav className={classes.drawer} aria-label="mailbox folders">
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation="css">
<Drawer // this one is for mobile
container={container}
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer // This one is for desktop
classes={{
paper: classes.drawerPaper,
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
{/* This is where my components renders */}
{
drawHelper === true && (<Draw/>)
}
<div className={classes.toolbar} />
{props.children}
</main>
</div>
);
}
The variable drawHelper in your code is instantiated on every render. You'd want to use React's state to make sure your drawHelper's value is preserved on the next re-renders.
const [drawHelper, toggleDrawHelper] = React.useState(false)
function toggleDraw() {
toggleDrawHelper(!drawHelper);
}

My content gets shifted to the right side when in mobile mode

Making a Drawer in React.Js with Material-UI.
When I switch to the mobile view, the Drawer content automatically shifts to the right side with a lot of margin.
It works fine the in desktop view.
I am not able to find the error in this code.
Could anyone guide me how can I solve this issue?
The code for the drawer:
import React from 'react';
[import PropTypes from 'prop-types';
import AppBar from '#material-ui/core/AppBar';
import CssBaseline from '#material-ui/core/CssBaseline';
import Divider from '#material-ui/core/Divider';
import Drawer from '#material-ui/core/Drawer';
import Hidden from '#material-ui/core/Hidden';
import IconButton from '#material-ui/core/IconButton';
import List from '#material-ui/core/List';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { makeStyles, useTheme } from '#material-ui/core/styles';
import { ListItem } from '#material-ui/core';
import { Data } from './data';
const drawerWidth = 240;
const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
},
drawer: {
\[theme.breakpoints.up('sm')\]: {
width: drawerWidth,
flexShrink: 0,
},
},
appBar: {
marginLeft: drawerWidth,
\[theme.breakpoints.up('sm')\]: {
width: `calc(100% - ${drawerWidth}px)`,
},
},
menuButton: {
marginRight: theme.spacing(2),
\[theme.breakpoints.up('sm')\]: {
display: 'none',
},
},
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
},
}));
function ResponsiveDrawer(props) {
const { container } = props;
const classes = useStyles();
const theme = useTheme();
const \[mobileOpen, setMobileOpen\] = React.useState(false);
var \[index, changeindex\] = React.useState(1);
function handleDrawerToggle() {
setMobileOpen(!mobileOpen);
}
const drawer = (
<div>
<div className={classes.toolbar} />
<Divider></Divider>
<List>
<ListItem button primary full large onClick = {()=>changeindex(index = 1)} > <Typography variant="h6" noWrap>
Test 1
</Typography></ListItem>
<ListItem button primary full large onClick={() => changeindex(index = 2)} >
<Typography variant="h6" noWrap>
Test2
</Typography>
</ListItem>
<ListItem button primary full large onClick={() => changeindex(index = 3)} >
<Typography variant="h6" noWrap>
Test3
</Typography>
</ListItem>
</List>
</div>
);
console.log(props);
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<i class="material-icons">view_headline</i>
</IconButton>
<Typography variant="h6" noWrap>
Responsive drawer
</Typography>
</Toolbar>
</AppBar>
<nav className={classes.drawer} aria-label="mailbox folders">
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation="css">
<Drawer
container={container}
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer
classes={{
paper: classes.drawerPaper,
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
<div className={classes.toolbar} />
{/* {console.log(index)} */}
<Data index={index} ></Data>
</main>
</div>
);
}
ResponsiveDrawer.propTypes = {
/**
* Injected by the documentation to work in an iframe.
* You won't need it on your project.
*/
container: PropTypes.instanceOf(typeof Element === 'undefined' ? Object : Element),
};
export default ResponsiveDrawer;][1]
Example image

How to active side menu in react js

How to active side menu in react application. I am using ListItem for display side menu in my application. But in side menu when I click on ListItem then it will redirect to new page. So How can I selected or active side menu. I tried to active using state but page is redirect to another page. So state will not work.
//import React from "react";
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import { withStyles } from "#material-ui/core/styles";
import Drawer from "#material-ui/core/Drawer";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import List from "#material-ui/core/List";
import CssBaseline from "#material-ui/core/CssBaseline";
import Typography from "#material-ui/core/Typography";
import Divider from "#material-ui/core/Divider";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import ChevronLeftIcon from "#material-ui/icons/ChevronLeft";
import ChevronRightIcon from "#material-ui/icons/ChevronRight";
import ListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import ListItemText from "#material-ui/core/ListItemText";
import { Link } from "./Router";
import TimerIcon from "#material-ui/icons/Timer";
import AssignmentIcon from "#material-ui/icons/Assignment";
import ReportIcon from "#material-ui/icons/Report";
import TimelineIcon from "#material-ui/icons/Timeline";
import TodoIcon from "#material-ui/icons/PlayCircleFilledWhite";
const drawerWidth = 240;
const styles = theme => ({
root: {
display: "flex"
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(["width", "margin"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
appBarShift: {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(["width", "margin"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen
})
},
menuButton: {
marginLeft: 12,
marginRight: 36
},
hide: {
display: "none"
},
drawer: {
width: drawerWidth,
flexShrink: 0,
whiteSpace: "nowrap"
},
drawerOpen: {
width: drawerWidth,
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen
})
},
drawerClose: {
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
overflowX: "hidden",
width: theme.spacing.unit * 7 + 1,
[theme.breakpoints.up("sm")]: {
width: theme.spacing.unit * 9 + 1
}
},
toolbar: {
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
padding: "0 8px",
...theme.mixins.toolbar
},
content: {
flexGrow: 1,
padding: theme.spacing.unit * 3
}
});
class MiniDrawer extends React.Component {
state = {
open: false,
selectedIndex: 0
};
handleDrawerOpen = () => {
this.setState({ open: true });
};
handleDrawerClose = () => {
this.setState({ open: false });
};
render() {
const { classes, theme } = this.props;
return (
<Fragment>
<CssBaseline />
<AppBar
position="fixed"
className={classNames(classes.appBar, {
[classes.appBarShift]: this.state.open
})}
>
<Toolbar disableGutters={!this.state.open}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerOpen}
className={classNames(classes.menuButton, {
[classes.hide]: this.state.open
})}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" color="inherit" noWrap>
Initio Technologies
</Typography>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
className={classNames(classes.drawer, {
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open
})}
classes={{
paper: classNames({
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open
})
}}
open={this.state.open}
>
<div className={classes.toolbar}>
<IconButton onClick={this.handleDrawerClose}>
{theme.direction === "rtl" ? (
<ChevronRightIcon />
) : (
<ChevronLeftIcon />
)}
</IconButton>
</div>
<Divider />
<List>
<ListItem
button
key="Time Tracker"
component={Link}
to="/timetracker"
>
<ListItemIcon>
<TimerIcon />
</ListItemIcon>
<ListItemText primary="Time Tracker" />
</ListItem>
<ListItem
button
key="Project"
component={Link}
to="/project"
>
<ListItemIcon>
<AssignmentIcon />
</ListItemIcon>
<ListItemText primary="Project" />
</ListItem>
<ListItem button key="Kanban" component={Link} to="/kanban">
<ListItemIcon>
<TimelineIcon />
</ListItemIcon>
<ListItemText primary="Kanban" />
</ListItem>
<ListItem
button
key="Todo"
component={Link}
to="/todo"
>
<ListItemIcon>
<TodoIcon />
</ListItemIcon>
<ListItemText primary="Todo" />
</ListItem>
<ListItem button key="Reports" component={Link} to="/reports">
<ListItemIcon>
<ReportIcon />
</ListItemIcon>
<ListItemText primary="Reports" />
</ListItem>
</List>
<Divider />
</Drawer>
</Fragment>
);
}
}
MiniDrawer.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired
};
export default withStyles(styles, { withTheme: true })(MiniDrawer);
Anyone please suggest me a solution.
Thanks
There are two ways to what I can think:
Have a Wrapper Component something like this:
const Wrapper = () => (
<React.Fragment>
<Sidebar />
<Content />
</React.Fragment>
)
In Content you can use route based rendering to switch the content. And thus manage state in sidebar.
Get the location of where your app is and setting the active item using this key.
You can use react-router to handle routing.
Hope this helps!

Resources