Categories
Code Learn

Mapping Nav Components into a Navbar Implementing React Router

I was tinkering with a way to map Nav Components into a navigation bar in React by utilising map() function. The original code looks like the following:

import React from 'react';
import Navbar from './components/Navbar';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Home from './components/Home';
import Report from './components/Report';
import Contact from './components/Contact';
import About from './components/About';
import Post from './components/Post';

function App() {
  return (
    <BrowserRouter>
      <div className="App">
      <Navbar />
          <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/report" component={Report} />
            <Route path="/about" component={About} />
            <Route path="/contact" component={Contact} />
            <Route path="/posts/:post_id" component={Post} />
          </Switch>
      </div>
    </BrowserRouter>
  );
}

We can do an abstraction for the following lines of code:

<Route exact path="/" component={Home} />
<Route path="/report" component={Report} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />

To do so, we can create an array of objects with properties of name and path. Let’s call it m

const menuItems = [
  { name: `Home`, path: `/` },
  { name: `Report`, path: `/report` },
  { name: `About`, path: `/about` },
  { name: `Contact`, path: `/contact` },
  { name: `Post`, path: `/posts/:post_id` }
];

Inside <Switch> we can map each item inside menuItems to create multiple <Route> components. We can do it this way.

<Switch>
  {menuItems.map(item =>
    item.name == `Home` ? (
      <Route exact path={item.path} component={item.name} />
    ) : (
      <Route path={item.path} component={item.name} />
    )
  )}
</Switch>;

We get a bug though. Our assignment component = {item.name} returns undefined because the component needs to be a componentName and not a String. How do we solve this? Following a suggestion for this thread on StackOverflow, one way to solve this problem is to create a new object with properties looks like the following.

const COMPONENT_MAP = {
  Home: Home,
  Report: Report,
  About: About,
  Contact: Contact,
  Post: Post
};

It’s almost like an identity function (I think?). Although, I’m unsure if that is what an identity function should be. We can then modify our function to get the correct output

<Switch>
  {menuItems.map(item =>
    item.name == `Home` ? (
      <Route exact path={item.path} component={COMPONENT_MAP[item.name]} /> //COMPONENT_MAP[item.name] returns componentName
    ) : (
      <Route path={item.path} component={COMPONENT_MAP[item.name]} />
    )
  )}
</Switch>;

Here is the final refactored code:

import React from "react";
import Navbar from "./components/Navbar";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./components/Home";
import Report from "./components/Report";
import Contact from "./components/Contact";
import About from "./components/About";
import Post from "./components/Post";

const menuItems = [
  { name: `Home`, path: `/` },
  { name: `Report`, path: `/report` },
  { name: `About`, path: `/about` },
  { name: `Contact`, path: `/contact` },
  { name: `Post`, path: `/posts/:post_id` }
];

const COMPONENT_MAP = {
  Home: Home,
  Report: Report,
  About: About,
  Contact: Contact,
  Post: Post
};

export default function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Navbar />
        <Switch>
          {menuItems.map(item =>
            item.name == `Home` ? (
              <Route
                exact
                path={item.path}
                component={COMPONENT_MAP[item.name]}
              />
            ) : (
              <Route path={item.path} component={COMPONENT_MAP[item.name]} />
            )
          )}
        </Switch>
      </div>
    </BrowserRouter>
  );
}

Let me know if there is a better way to do this.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.