Building a React Redux compatible hamburger menu – Setup (part 1)

Most of the Web Apps these days need to support a special navigation for mobile devices (smaller screens) and sometimes …

Most of the Web Apps these days need to support a special navigation for mobile devices (smaller screens) and sometimes that option looks a little different from the one implemented on larger screens.

One of the most popular methods is to implement the hamburger menu which would look like this:

Uber might be a good example:

Basically, there are two ways to implement the hamburger navigation bar menu and those options are:

  • Implementing something from scratch, this means adding some CSS effects for the slide in and slide out effect, as well as logic for the javascript in order to control when the panel is open/closed, and media queries to control when the menu is going to be shown.
  • Using an existent library makes it easy to plug and play and customize it based on our needs.

Should we use the first approach or the second?, Why not? Will the world end? How do I know?, lots of questions, right? what if we just look into the different approaches and then leave it up to you to decide which one fits your project best.

In the first two parts of these tutorials, we will learn how to do it using the first option (building something from scratch – custom implementation).

NOTE: In this first part of the tutorial we are going to set up our new project using the create-react-app script to initialize our project quickly, I would recommend you follow this first part and implement it in a separate project, in that case you’d be able to see it actually working. I have experienced that when you implement a new feature in an existent project sometimes it does not work because you’re either using different dependency versions or there is a conflict with one of your existent dependencies.

Dependencies utilized in this project:

  • React – ^16.6.3
  • react-router-dom – ^4.3.1
  • react-dom – ^16.6.3
  • react-redux – ^5.1.1
  • react-router-dom – ^4.3.1
  • redux – ^4.0.1
  • redux-thunk – ^2.3.0
  • node-sass – ^4.10.0

This is what you’re going to build in the next tutorial, once you’ve set everything up for our new project

Let’s get started

We can run one command and initialize our project real quick using the npm command create-react-app

npx create-react-app burger-menu
cd burger-menu
npm start

Since this example uses React Redux we need to install that dependency:

npm install --save redux
npm install --save react-redux

We’ll use use Redux Thunk

npm install redux-thunk --save

And we want to use routes so we are going to install React Router

npm install --save react-router-dom

In this example, we are going to use SASS so we need to install that dependency as well

npm install node-sass --save

Let’s use a less generic name for the main div where the React application is mounted in the public/index.html file:

-    <div id="root"></div>
+    <div id="rootContainer"></div>

As we are going to use SCSS we could remove the main src/App.css that was created automatically when we initialized the base project with the create-react-app NPM command.

Also we are going to remove the main src/App.js and we are going to use a custom one so we can easily remove it from the terminal:

rm src/App.js

Based on a complex project I worked on in the past, I decided to create common layouts from the beginning so we could easily customize and re-use them in the case fo this example, we have a login and dashboard/panel layouts due to most of the time they have structure/CSS differences, in this case as I’m using React Router 4 I followed a similar approach to the one in this tutorial https://simonsmith.io/reusing-layouts-in-react-router-4/

First, we need to create a new component for the default layout

src/DefaultLayout.js

import React from 'react';
import { Route } from "react-router-dom";
import "./DefaultLayout.scss";

const DefaultLayout = ({component: Component, ...rest}) => {

  return (
    <Route {...rest} render={matchProps => (
      <div className="default-layout">
        <HamburgerIconContainer showSideBarMenu />

        <NavBarContainer/>

        <div className="main-container">
          <Component {...matchProps} />
        </div>
        <div className="layout-footer">Footer</div>
      </div>
    )} />
  )
};

export default DefaultLayout;

The CSS should look like this:

src/DefaultLayout.scss

.default-layout {
}

body {
  padding: 0;
  margin: 0;
  font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}

header, footer {
  background: white;
  color: gray;
}

.main-container {
  padding: 10px;
}

.layout-footer {
  padding: 10px;
  background: gray;
  color: white;
}

Now, we are going to use that layout for

src/routes.js

import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import DefaultLayout from "./DefaultLayout";
import { Provider } from "react-redux";

const NotFound = () => (
  <h1>
    Not found
  </h1>
);

const About = () => (
  <h1>
    About us
  </h1>
);

const Contact = () => (
  <h1>
    Contact us
  </h1>
);

const Home= () => (
  <h1>
    Home Dashboard
  </h1>
);

export default (store) => {
  return (
    <Provider store={store}>
      <BrowserRouter>
        <Switch>
          <DefaultLayout exact path="/" component={Home} />
          <DefaultLayout exact path="/about" component={About}/>
          <DefaultLayout exact path="/contact" component={Contact}/>
          <Route path="*" component={NotFound}/>
        </Switch>
      </BrowserRouter>
    </Provider>
  );
}

NOTE: In case that you are using React Router 3 you can use different layouts – following a similar structure/nesting just like this https://stackoverflow.com/a/33999957/1849051):

  <Route component={Layout1}>
    <Route path="about" component={About}/>
    <Route path="faq" component={Contact}/>
  </Route>

Now, we need to specify that we are going to use these routes and components:

src/index.js

import React from "react";
import { render } from "react-dom";
import Router from "./routes";
import store from "./store";
import * as serviceWorker from './serviceWorker';

const run = () => {
  render(Router(store), document.getElementById("rootContainer"));
}

if (["complete", "loaded", "interactive"].includes(document.readyState) && document.body) {
  run();
} else {
  document.addEventListener("DOMContentLoaded", run, false);
}

serviceWorker.unregister();

If you look into the previous file, you can see we are using the store as we planned to use React Redux

Let’s look into the React Redux set up then

Let’s start by creating the main store

src/store.js

import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";

import rootReducer from "./reducers/RootReducer";

let createStoreWithMiddleware = applyMiddleware(
  thunk
)(createStore);

export default createStoreWithMiddleware(
  rootReducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

Now, before creating our reducers, we may create an action as an example:
Let’s create a folder called src/actions/

src/actions/BurgerAction.js

export const SHOW_MOBILE_NAVIGATION_MENU = "SHOW_MOBILE_NAVIGATION_MENU";
export const HIDE_MOBILE_NAVIGATION_MENU = "HIDE_MOBILE_NAVIGATION_MENU";

export function showMobileNavigationMenu() {
  return {
    type: SHOW_MOBILE_NAVIGATION_MENU,
  };
}
export function hideMobileNavigationMenu() {
  return {
    type: HIDE_MOBILE_NAVIGATION_MENU,
  };
}

And now we need to declare our reducers(listeners), as an example let’s create one called SimpleReducer
Create a new folder called reducers within the src folder for that.

src/reducers/RootReducer.js

import { combineReducers } from "redux";
import BurgerMenuReducer from "./BurgerMenuReducer";
export default combineReducers({
  BurgerMenuReducer
});

And now our first reducer should look like this:

src/reducers/burgerMenuReducer.js

import {
  SHOW_MOBILE_NAVIGATION_MENU,
  HIDE_MOBILE_NAVIGATION_MENU,
} from "../actions/BurgerAction";

const DEFAULT_STATE = {
  visibleMobileMenu: false
}

export default (state = DEFAULT_STATE, action) => {
  switch(action.type){
    case SHOW_MOBILE_NAVIGATION_MENU:
      return { ...state, visibleMobileMenu: true };
    case HIDE_MOBILE_NAVIGATION_MENU:
      return { ...state, visibleMobileMenu: false };
    default:
      return state;
  }
};

At this point the functionality looks like this:

What next?

Building a React Redux compatible hamburger menus for small devices – custom implementation (part 2)

Keep reading

More >