I'm looking at the using the following example code for the frame around a small app:
https://material-ui.com/components/drawers/#MiniDrawer.js
In trying to refactor the elements into separate components I've come across an issue. Cutting and pasting the code into a brand new create-react-app
app work fine. However, as soon as I try to refactor out the AppBar and the Drawer into separate components, I suspect that they lose track of each other.
My version on codesandbox.io.
I've started by extracting the useStyles
into a separate file:
// useStyles.js
import { makeStyles } from '@material-ui/core/styles';
const drawerWidth = 240;
const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
},
appBar: {
zIndex: theme.zIndex.drawer + 100,
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),
...theme.mixins.toolbar,
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
},
}));
export default useStyles;
... and the AppBar and Drawers into their own files:
// MyUIAppBar.js
import React from 'react';
import clsx from 'clsx';
// ...
import { Mail, Notifications, Menu } from '@material-ui/icons';
function MyUIAppBar(props) {
const classes = props.classes;
const toggleDrawer = props.toggleDrawer;
const open = props.open;
return(
<AppBar
position="fixed"
className={clsx(classes.appBar, {
[classes.appBarShift]: open,
})}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={toggleDrawer}
edge="start"
className={clsx(classes.menuButton, {
[classes.hide]: open,
})}
>
<Menu />
</IconButton>
<Typography component="h1" variant="h6" color="inherit" noWrap className={classes.title}>
MDS MyUI
</Typography>
// ...
</Toolbar>
</AppBar>
);
}
export default MyUIAppBar;
and
// MyUIDrawer.js
import React from 'react';
import clsx from 'clsx';
import Drawer from '@material-ui/core/Drawer';
import IconButton from '@material-ui/core/IconButton';
import { ChevronLeft, ChevronRight } from '@material-ui/icons';
function MyUIDrawer(props) {
const classes = props.classes;
const toggleDrawer = props.toggleDrawer;
const open = props.open;
return (
<Drawer
variant="permanent"
className={clsx(classes.drawer, {
[classes.drawerOpen]: open,
[classes.drawerClose]: !open,
})}
classes={{
paper: clsx({
[classes.drawerOpen]: open,
[classes.drawerClose]: !open,
}),
}}
open={open}
onToggle={toggleDrawer}
>
<div className={classes.toolbar}>
<IconButton onClick={toggleDrawer}>
{open ? <ChevronRight /> : <ChevronLeft />}
</IconButton>
</div>
{props.children}
</Drawer>
);
}
export default MyUIDrawer;
and in the main App file I've had to pass theme, open, classes, and toggleDrawer in as props to both components:
// App.js
import React from 'react';
import useStyles from './styles';
import WebUIDrawer from './components/WebUIDrawer';
import WebUIAppBar from './components/WebUIAppBar';
import { useToggle } from './hooks';
import { useTheme } from '@material-ui/core/styles';
// ... various imports here
export default function App() {
const classes = useStyles();
const theme = useTheme();
const [ open, toggleDrawer ] = useToggle(true);
return (
<div className={classes.root}>
<CssBaseline />
<WebUIAppBar theme={theme} open={open} classes={classes} toggleDrawer={toggleDrawer}/>
<WebUIDrawer open={open} classes={classes} toggleDrawer={toggleDrawer} >
<List>
{['Eeny', 'Meeny', 'Miney', 'Mo'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <MoveToInbox /> : <Mail />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</WebUIDrawer>
<Main classes={classes}/>
</div>
);
}
The result sort of works, but there seems to be an issue with the layering and offset of the menu. My suspicion is that somehow the theme isn't updating between the two. Is there a better way to share the style info between the components? Would it be possible to use React context to do this? Frankly, I'm stumped. :-(