How i can pass routes path from the array and update them on Click call?

Viewed 31

I am new to React JS. I am making a project in which I initially create an array of menu items to store some of the props of the menu like its state or path etc.

Now I want to implement a router function but now I cannot understand how I can implement that using the array and map function. I tried something like <Route path={getPath} > <MenuItem /> </Route> but i dint get any result.Any way to solve this issue?

Required Functionality: When I click on any navbar link, the router takes me to that path and shows a specific component and also updates Menu State to true so that the user knows he is on that current page.

I am attaching the code below :

import React from 'react'
import Menu from './components/Menu'
import Logo from './components/Logo'
import LeftSide from '../src/components/LeftSide'
import About from './components/About'
import Projects from './components/Projects'
import Contact from './components/Contact'
import { useState } from 'react'
import RightSide from './components/RightSide'
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";

function App() {

  const [menuItems, setMenuItems] = useState([
    {
      id: 1,
      menuItemName: 'Home',
      path: '/',
      menuItemState: true
    },
    {
      id: 2,
      menuItemName: 'About',
      path: '/about',
      menuItemState: false
    },
    {
      id: 3,
      menuItemName: 'Projects',
      path: '/projects',
      menuItemState: false
    },
    {
      id: 4,
      menuItemName: 'Contact',
      path: '/contact',
      menuItemState: false
    }
  ])

  // get menu path from menuItems on click
  const getMenuPath = (menuItem) => {
    setMenuItems(
      menuItems.map(item => {
        if (item.id === menuItem.id) {
          item.menuItemState = true
          return item.path;
        } else {
          item.menuItemState = false
        }
    }
      )
    )
  }

  
  return (
    <Router>
      <div className="App">
        <div className="relative flex lg:flex-row space-y-0 md:flex-col flex-col h-screen">
          <div className="flex flex-col space-y-24 md:space-y-30 lg:space-y-56 p-4 bg-[#1c1c1c] w-full">
            <Logo />
            <LeftSide />
            <div className='flex flex-row p-1'></div>
          </div>
          <div className="text-white bg-[#171717] p-4 w-full">
            <div className="flex flex-col lg:space-y-36 md:space-y-16 space-y-0 p-1 w-full">
              <div className="hidden md:hidden lg:block">
              <Menu menuItems={menuItems} onClick={getMenuPath}/>
                <Switch>
                  <Route path="/about" exact component={<About />}/>
                  <Route path="/projects" exact component={<Projects />}/>
                  <Route path="/contact" exact component={<Contact />}/>
                </Switch>
              </div>
              <RightSide />
            </div>
          </div>
        </div>
      </div>
    </Router>
  );
}

export default App;
1 Answers

Issues

I see a couple of issues:

  1. You are mutating the menuItems state in the getMenuPath callback handler.
  2. The getMenuPath callback handler isn't dispatching any navigation action. If Menu isn't rendering Link components to effect a routing/navigation action then this code will need to issue an imperative navigation action.
  3. The react-router-dom@5 Route components take a reference to a React component on the component prop, not JSX.

Solution

  1. Fix the mutation by mapping the previous state to the next state, creating new object references for any state, and nested state, that is being updated.
  2. If necessary, you'll need to move the Router component up the ReactTree so that the App component can use the useHistory hook and issue the imperative navigation action to the selected menu item path.
  3. Use component={Component} instead of component={<Component />} for the Route components.

Example:

import { BrowserRouter as Router } from "react-router-dom";
import App from './App';

...

<Router>
  <App />
</Router>

...

import { Route, Link, Switch, useHistory } from "react-router-dom";

function App() {
  const history = useHistory();

  const [menuItems, setMenuItems] = useState([....]);

  // get menu path from menuItems on click
  const getMenuPath = (menuItem) => {
    setMenuItems(menuItems => menuItems.map(item => ({
      ...item,                               // <-- shallow copy previous state
      menuItemState: item.id === menuItem.id // <-- update property
    })));
    history.push(menuItem.path);             // <-- issue imperative navigation
  };
  
  return (
    <div className="App">
      <div className="....">
        <div className="....">
          <Logo />
          <LeftSide />
          <div className='....'></div>
        </div>
        <div className="....">
          <div className="....">
            <div className="hidden md:hidden lg:block">
              <Menu menuItems={menuItems} onClick={getMenuPath}/>
              <Switch>
                <Route path="/about" exact component={About} />
                <Route path="/projects" exact component={Projects} />
                <Route path="/contact" exact component={Contact} />
              </Switch>
            </div>
            <RightSide />
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;
Related