React Material-UI Drawer using hooks

Andrew Bliss
3 min readMar 16, 2019

--

Full code example: https://gitlab.com/sundry/react/react-material-ui-drawer

Creating reusable components is essential in building quality applications. React and Material-UI make it seamless to integrate styled UI components into your own application. We will be creating a Drawer component that is controlled by React hooks. More specifically, useState and useContext .

// components/Drawer/Drawer.jsimport React, { useState, useContext } from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import SwipeableDrawer from '@material-ui/core/SwipeableDrawer';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import IconButton from '@material-ui/core/IconButton';
import MenuIcon from '@material-ui/icons/Menu';
import Typography from '@material-ui/core/Typography';
import Divider from '@material-ui/core/Divider';
import SettingsContext from 'context/Settings/SettingsContext';
const styles = theme => ({
root: {
position: 'fixed',
top: 5,
right: 5,
},
drawer: {
width: 250,
},
drawerItem: {
padding: 15,
},
textField: {
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit;
width: 200,
},
});
const Drawer = ({ classes, align = 'right'}) => {
const context = useContext(SettingsContext);
const [open, setOpen] = useState(false);
return (
<div className={classes.root}>
<IconButton
color="inherit"
aria-label="Settings"
onClick={() => setOpen(true)}
>
<Typography>
<MenuIcon fontSize="large" />
</Typography>
</IconButton>
<SwipeableDrawer
anchor={align}
open={open}
onClose={() => setOpen(false)}
onOpen={() => setOpen(true)}
>
<div className={classes.drawer}>
<Typography
component="h5"
variant="h6"
align="center"
style={{
margin: 10,
}}
>
Settings
</Typography>
<Divider />
<div className={classes.drawerItem}>
<FormControlLabel
control={
<Switch
checked={context.darkMode}
onChange={() => context.onSetDarkMode(!context.darkMode)}
/>
}
label="Dark Mode"
/>
</div>
</div>
</SwipeableDrawer>
</div>
);
};
Drawer.propTypes = {
classes: PropTypes.object,
align: PropTypes.string,
};
export default withStyles(styles)(Drawer);

Using the useContext hook, we can tell this component about our application context. There are several different approaches to this, such as, Redux, or MobX. We will be using React hooks for this application state.

const context = useContext(SettingsContext);// We can use a variable called darkMode on the context
// We also have a onSetDarkMode function to change the context
<Switch
checked={context.darkMode}
onChange={() => context.onSetDarkMode(!context.darkMode)}
/>

Using the useState hook, we can integrate a local state that indicates if the drawer is open or not.

const [open, setOpen] = useState(false);// First we can show a menu icon, that when clicked on it will call // setOpen(true)<IconButton
color="inherit"
aria-label="Settings"
onClick={() => setOpen(true)}
>
<Typography>
<MenuIcon fontSize="large" />
</Typography>
</IconButton>
// Second we can render a Material UI Drawer<SwipeableDrawer
anchor={align}
open={open}
onClose={() => setOpen(false)}
onOpen={() => setOpen(true)}
>

Using context requires that we create a context object and wrap the parts of our application using a provider.

// context/Settings/SettingsContext.jsimport React from 'react';export default React.createContext({
darkMode: false,
});
// context/Settings/SettingsProvider.jsimport React, { useState } from 'react';
import SettingsContext from './SettingsContext';
// Save settings to local storage
const storage = {
getItem(key) {
if (localStorage) {
return localStorage.getItem(key);
}
},
setItem(key, value) {
if (localStorage) {
return localStorage.setItem(key, value);
}
},
};
const SettingsProvider = props => {
const [darkMode, setDarkMode] = useState(
storage.getItem('darkMode') === 'true'
);
const onSetDarkMode = darkMode => {
setDarkMode(darkMode);
storage.setItem('darkMode', darkMode);
};
return (
<SettingsContext.Provider
value={{
darkMode,
onSetDarkMode,
}}
>
{props.children}
</SettingsContext.Provider>
);
};
export default SettingsProvider;

Now that we have our context, we can wrap the parts of our application where we want to use context.

import React from 'react';
import ReactDOM from 'react-dom';
import App from 'components/App/App';
import * as serviceWorker from 'helpers/serviceWorker';
import SettingsProvider from 'context/Settings/SettingsProvider';
ReactDOM.render(
<SettingsProvider>
<App />
</SettingsProvider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

Creating the application logic to show the Drawer. Using the context we can show either a default theme or dark theme.

import React, { useContext } from 'react';
import CssBaseline from '@material-ui/core/CssBaseline';
import { MuiThemeProvider } from '@material-ui/core/styles';
import Drawer from 'components/Drawer/Drawer';
import SettingsContext from 'context/Settings/SettingsContext';
const App = () => {
return (
<MuiThemeProvider>
<CssBaseline />
<Drawer align="left" />
</MuiThemeProvider>
);
};
export default App;

--

--