React - Material-UI Responsive AppBar and Drawer
For those who're working with material-UI V1 and higher, using breakpoints is the best way to make responsive layouts.
Breakpoints:
- xs, extra-small: 0px or larger
- sm, small: 600px or larger
- md, medium: 960px or larger
- lg, large: 1280px or larger
- xl, xlarge: 1920px or larger
So, for changing React rendering tree you should pass different breakpoint parameter value into breakpoints.up()
in the JSS:
navIconHide: {
[theme.breakpoints.up('md')]: {
display: 'none',
},
},
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
[theme.breakpoints.up('md')]: {
position: 'relative',
},
},
The whole Drawer source code follows below.
import React from 'react';
import PropTypes from 'prop-types';
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 Typography from '@material-ui/core/Typography';
import IconButton from '@material-ui/core/IconButton';
import Hidden from '@material-ui/core/Hidden';
import Divider from '@material-ui/core/Divider';
import MenuIcon from '@material-ui/icons/Menu';
import { mailFolderListItems, otherMailFolderListItems } from './tileData';
const drawerWidth = 240;
const styles = theme => ({
root: {
flexGrow: 1,
height: 430,
zIndex: 1,
overflow: 'hidden',
position: 'relative',
display: 'flex',
width: '100%',
},
appBar: {
position: 'absolute',
marginLeft: drawerWidth,
[theme.breakpoints.up('md')]: {
width: `calc(100% - ${drawerWidth}px)`,
},
},
navIconHide: {
[theme.breakpoints.up('md')]: {
display: 'none',
},
},
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
[theme.breakpoints.up('md')]: {
position: 'relative',
},
},
content: {
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing.unit * 3,
},
});
class ResponsiveDrawer extends React.Component {
state = {
mobileOpen: false,
};
handleDrawerToggle = () => {
this.setState(state => ({ mobileOpen: !state.mobileOpen }));
};
render() {
const { classes, theme } = this.props;
const drawer = (
<div>
<div className={classes.toolbar} />
<Divider />
<List>{mailFolderListItems}</List>
<Divider />
<List>{otherMailFolderListItems}</List>
</div>
);
return (
<div className={classes.root}>
<AppBar className={classes.appBar}>
<Toolbar>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerToggle}
className={classes.navIconHide}
>
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" noWrap>
Responsive drawer
</Typography>
</Toolbar>
</AppBar>
<Hidden mdUp>
<Drawer
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={this.state.mobileOpen}
onClose={this.handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden smDown implementation="css">
<Drawer
variant="permanent"
open
classes={{
paper: classes.drawerPaper,
}}
>
{drawer}
</Drawer>
</Hidden>
<main className={classes.content}>
<div className={classes.toolbar} />
<Typography noWrap>{'You think water moves fast? You should see ice.'}</Typography>
</main>
</div>
);
}
}
ResponsiveDrawer.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
};
export default withStyles(styles, { withTheme: true })(ResponsiveDrawer);
You could listen for screen size changes in componentWillMount like this, I'm sure there are better methods but this works.
toggleOpenDrawer = () => {
if (!this.state.mobile) {
return;
}
this.setState({
open: !this.state.open
})
}
setSmall = () => {
this.setState({open: false, docked: false, mobile: true})
}
setLarge = () => {
this.setState({open: true, docked: true, mobile: false})
}
componentWillMount() {
const mediaQuery = window.matchMedia('(min-width: 768px)');
if (mediaQuery.matches) {
this.setLarge()
} else {
this.setSmall()
}
mediaQuery.addListener((mq) => {
if (mq.matches) {
this.setLarge()
} else {
this.setSmall()
}
});
}