Search code examples
reactjsreact-router-domdry

How can I pass the component's name from another component's object to Route in React Router Dom?


I'm learning React and while following several tutorials I like to go back and learn how I can make it D.R.Y so given the following app.js:

app.js

import React from 'react'
import './App.css'

import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'

// Components
import Navbar from './components/Navbar'
import { Home, Reports, Products, Team, Messages, Support } from './pages'

function App() {
  return (
    <Router>
      <Navbar />
      <Switch>
        <Route path="/" exact component={Home} />
        <Route path="/reports" exact component={Reports} />
        <Route path="/products" exact component={Products} />
        <Route path="/team" exact component={Team} />
        <Route path="/messages" exact component={Messages} />
        <Route path="/support" exact component={Support} />
      </Switch>
    </Router>
  )
}

export default App

I noticed that the Route's are also replicated to what I have for declaring my sidebar's title:

SidebarData.js:

import React from 'react'

import * as FaIcons from 'react-icons/fa'
import * as AiIcons from 'react-icons/ai'
import * as IoIcons from 'react-icons/io'

const SidebarData = [
  { title: 'Home', path: '/', icon: <AiIcons.AiFillHome />, cName: 'nav-text' },
  { title: 'Reports', path: '/reports', icon: <IoIcons.IoIosPaper />, cName: 'nav-text' },
  { title: 'Products', path: '/products', icon: <FaIcons.FaCartPlus />, cName: 'nav-text' },
  { title: 'Team', path: '/team', icon: <IoIcons.IoMdPeople />, cName: 'nav-text' },
  { title: 'Messages', path: '/messages', icon: <FaIcons.FaEnvelopeOpenText />, cName: 'nav-text' },
  { title: 'Support', path: '/support', icon: <IoIcons.IoMdHelpCircle />, cName: 'nav-text' },
]

export default SidebarData

Given that the code is replicated I've been trying to map over the component SidebarData in the Route's component declaration with:

Attempt 1:

import React from 'react'
import './App.css'

import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'

// Components
import Navbar from './components/Navbar'
import SidebarData from './components/SidebarData'
import { Home, Reports, Products, Team, Messages, Support } from './pages'

function App() {
  return (
    <Router>
      <Navbar />
      <Switch>
        {SidebarData.map(({ path, title }, i) => (
          <Route key={i} path={path} exact component={title} />
        ))}
      </Switch>
    </Router>
  )
}

export default App

but I get an error and notice that when calling the component it does not work and in VSC my components brought in:

import { Home, Reports, Products, Team, Messages, Support } from './pages'

are greyed out.

Attempt 2:

I thought maybe I could build a reference in SidebarData by bringing in the components there:

SidebarData.js:

import React from 'react'

import * as FaIcons from 'react-icons/fa'
import * as AiIcons from 'react-icons/ai'
import * as IoIcons from 'react-icons/io'

import { Home, Reports, Products, Team, Messages, Support } from '../pages'

const SidebarData = [
  { title: 'Home', comp: <Home />, path: '/', icon: <AiIcons.AiFillHome />, cName: 'nav-text' },
  {
    title: 'Reports',
    comp: <Reports />,
    path: '/reports',
    icon: <IoIcons.IoIosPaper />,
    cName: 'nav-text',
  },
  {
    title: 'Products',
    comp: <Products />,
    path: '/products',
    icon: <FaIcons.FaCartPlus />,
    cName: 'nav-text',
  },
  { title: 'Team', comp: <Team />, path: '/team', icon: <IoIcons.IoMdPeople />, cName: 'nav-text' },
  {
    title: 'Messages',
    comp: <Messages />,
    path: '/messages',
    icon: <FaIcons.FaEnvelopeOpenText />,
    cName: 'nav-text',
  },
  {
    title: 'Support',
    comp: <Support />,
    path: '/support',
    icon: <IoIcons.IoMdHelpCircle />,
    cName: 'nav-text',
  },
]

export default SidebarData

then changing the map in app.js:

{SidebarData.map(({ path, comp }, i) => (
          <Route key={i} path={path} exact component={comp} />
        ))}

but that doesn't work either with the error of:

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

Check the render method of Router.Consumer.

I've tried to research this but I think my failure is due to not knowing what this would be called in React. Reading the following have not worked:

How can I turn the referenced title from the component SidebarData into a component reference in my Route when I map through SidebarData?


Solution

  • Good question, try this:

    const SidebarData = [
      // ...   
      {
        title: 'Reports',
        comp: Reports,
        path: '/reports',
        icon: <IoIcons.IoIosPaper />,
        cName: 'nav-text',
      },
      {
        title: 'Products',
        comp: Products,
        path: '/products',
        icon: <FaIcons.FaCartPlus />,
        cName: 'nav-text',
      },
      ...
    

    and where you're mapping, use the comp property like this:

    {
      SidebarData.map(({ path, comp }, i) => (
        <Route key={i} path={path} exact component={comp} />
      ));
    }
    

    and also since the comp property holds a reference to the components I think it's better to capitalize it like Comp.