DEV Community

Cover image for Building a navigation drawer with Material UI and React Router DOM
Rossano D'Angelo
Rossano D'Angelo

Posted on • Updated on

Building a navigation drawer with Material UI and React Router DOM

In the last article, I built a basic Drawer that, at the end of this article, will contain a full working navigation block.

Installing React Router DOM

At the root folder of football-almanac, I run

npm install react-router-dom @types/react-router-dom
Enter fullscreen mode Exit fullscreen mode

It'll install React Router DOM.

Designing the navigation

First three URLs that come in mind for an application like this are

  • / (the home page)
  • /standings
  • /teams

Implementing React Router DOM

Wrap everything!

To get started, I import BrowserRouter in index.tsx and I wrap the whole application within it.

......
import { BrowserRouter } from 'react-router-dom';
......
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

At this point, since the App component is going to grow too much, I split it into multiple components. This will help me to ensure a good level of isolation of them, also for testing purposes.

The Router object

I find very helpful defining my routes as an object like this

const Routes = [
  {
    path: [url],
    sidebarName: [label],
    icon: [material_ui_icon_name],
    component: [component_name],
  },
  ...
];
Enter fullscreen mode Exit fullscreen mode

In this way, I can define my router once and reuse it when I need, as a module.

I define my routes in Routes.tsx.

import React from 'react';

const Home: React.FC = () => {
  return (
    <h1>Home</h1>
  );
};

const Standings: React.FC = () => {
  return (
    <h1>Standings</h1>
  );
};

const Teams: React.FC = () => {
  return (
    <h1>Teams</h1>
  );
};

const Routes = [
  {
    path: '/',
    sidebarName: 'Home',
    component: Home
  },
  {
    path: '/standings',
    sidebarName: 'Standings',
    component: Standings
  },
  {
    path: '/teams',
    sidebarName: 'Teams',
    component: Teams
  },
];

export default Routes;
Enter fullscreen mode Exit fullscreen mode

For the moment I create some placeholder components (Home, Standings and Teams).

The NavigationBar

I create a new component subfolder, named NavigationBar. The new component is NavigationBar.tsx.

import React, { useState } from 'react';

import { NavLink, withRouter } from 'react-router-dom';
import Routes from '../App/Routes';

import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import {
  AppBar,
  Toolbar,
  Typography,
  IconButton,
  Drawer,
  MenuList,
  MenuItem,
  ListItemText,
 } from '@material-ui/core';
import MenuIcon from '@material-ui/icons/Menu';

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
    },
    menuButton: {
      marginRight: theme.spacing(2),
    },
    title: {
      flexGrow: 1,
    },
    drawer: {
      width: 300,
    },
    fullList: {
      width: 'auto',
    },
  }),
);

const NavigationBar: React.FC = (props: any) => {
  const classes = useStyles();
  const [isOpen, setIsOpen] = useState(false);
  const toggleDrawer = (open: boolean) => (
    event: React.KeyboardEvent | React.MouseEvent,
  ) => {
    if (
      event.type === 'keydown' &&
      ((event as React.KeyboardEvent).key === 'Tab' ||
        (event as React.KeyboardEvent).key === 'Shift')
    ) {
      return;
    }

    setIsOpen(open);
  };

  const activeRoute = (routeName: any) => {
    return props.location.pathname === routeName ? true : false;
  }

  return (
    <div>
      <div className={classes.root}>
        <AppBar position="static">
          <Toolbar>
            <IconButton edge="start" className={classes.menuButton} color="inherit" aria-label="menu" onClick={toggleDrawer(true)}>
              <MenuIcon />
            </IconButton>
            <Typography variant="h6" className={classes.title}>
              Football Almanac
            </Typography>
          </Toolbar>
        </AppBar>
      </div>
      <Drawer classes={{ paper: classes.drawer }} open={isOpen} onClose={toggleDrawer(false)}>
        <div
          className={classes.fullList}
          role="presentation"
          onClick={toggleDrawer(false)}
          onKeyDown={toggleDrawer(false)}
        >
          <MenuList>
            {Routes.map((prop, key) => {
              return (
                <NavLink to={prop.path} style={{ textDecoration: 'none' }} key={key}>
                  <MenuItem selected={activeRoute(prop.path)}>
                    <ListItemText primary={prop.sidebarName} />
                  </MenuItem>
                </NavLink>
              );
            })}
          </MenuList>
        </div>
      </Drawer>
    </div>
  );
};

export default withRouter(NavigationBar);
Enter fullscreen mode Exit fullscreen mode

To have the browser navigation available within this component, I used a higher-order component that comes with React Router DOM, withRouter.

It passes updated match, location, and history props to the wrapped component whenever it renders.

To learn more about withRender, take a look at the documentation.

App.tsx

import React from 'react';

import { Switch, Route } from 'react-router-dom';
import Routes from './Routes';

import NavigationBar from './NavigationBar/NavigationBar';

const App: React.FC = () => {
  return (
    <div>
      <NavigationBar />
      <Switch>
        {Routes.map((route: any) => (
          <Route exact path={route.path} key={route.path}>
            <route.component />
          </Route>
        ))}
      </Switch>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Understanding the following snippet is essential: since we can easily add and remove routes from an independent module, it's enough iterating it and create a Route for each route defined in the object.

<Switch>
  {Routes.map((route: any) => (
    <Route exact path={route.path} key={route.path}>
      <route.component />
    </Route>
  ))}
</Switch>
Enter fullscreen mode Exit fullscreen mode

The result is the following

Alt Text

What's next

In the next step I will create the home page that will display some data fetched from the APIs.

Useful resources

Top comments (1)

Collapse
 
abhisheksharmas04 profile image
abhisheksharmas04

could you please tell.. how to create drawer navigation using javascript syntax in react js.