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.