ch1maera
ch1maera

Reputation: 1447

Fetch Param from Route in Nav Bar, React-Router

I have a router set up in my App.js as follows:

import React from 'react';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
import NavBar from './nav-bar';
import Landing from './landing-page';
import Dashboard from './dashboard';
import Analysis from './analysis';
import '../style.scss';

const App = (props) => {
  return (
    <Router>
      <NavBar />
      <Switch>
        <Route exact path="/" component={Landing} />
        <Route path="/dashboard/:prodID/search" component={Dashboard} />
        <Redirect from="/dashboard/:prodID" to="/dashboard/:prodID/search" />
        <Route path="/dashboard/:prodID/analyze" component={Analysis} />
        <Route component={() => (
          <div id="error">
            <h1>404 ERROR</h1>
            <h2>Page not found</h2>
          </div>
        )}
        />
      </Switch>
    </Router>
  );
};

export default App;

and my NavBar component is set up as follows:

import React, { Component } from 'react';
import { NavLink, withRouter } from 'react-router-dom';
import { Navbar, Nav } from 'react-bootstrap';
import '../style.scss';

class NavBar extends Component {
  constructor(props) {
    super(props);

    this.state = {};
  }

  render() {
    return (
      <Navbar id="nav-bar" bg="dark" variant="dark">
        <Navbar.Brand href="/">
          My Project
        </Navbar.Brand>
        <Navbar.Collapse id="responsive-navbar-nav" className="justify-content-end">
          <Nav>
            <NavLink to="/dashboard/:prodID/search">Search</NavLink>
            <NavLink to="/dashboard/:prodID/analyze">Analyze</NavLink>
          </Nav>
        </Navbar.Collapse>
      </Navbar>
    );
  }
}

export default withRouter(NavBar);

I have two things that I'm trying to figure out:

  1. I want to be able to access the prodID route param within my NavBar component so that when a user clicks on the route, it will take the valid prodID and render the route correctly.
  2. I want to only display the NavLinks in NavBar if the user is on a route that has the prodID param. If they're on the home route / for example, the links wouldn't show up. But if they're on the route /dashboard/[valid prodID]/search, the links would show up.

How do I go about implementing this? I've looked at other posts on SO dealing with route params and nav bars, but none of them have answered my question. Any help is appreciated.

Upvotes: 4

Views: 2083

Answers (1)

szczocik
szczocik

Reputation: 1333

I believe you would have to move your navbar under each of the routes, so that it can be re-rendered and grab the correct params when the path changes.

In order to achieve it, you can create the Layout component which will wrap the component you pass and add a navbar to it:

// Layout.jsx
import React from "react";
import NavBar from './nav-bar';

export const Layout = () => {
  return (
    <div>
      <Navbar />
      <div>{children}</div>
    </div>
  );
};

Then in your App, you can wrap each component within the routes with the Layout component like so

// App.jsx    
import React from "react";
import {
  BrowserRouter as Router,
  Route,
  Switch,
  Redirect
} from "react-router-dom";
import NavBar from "./nav-bar";
import Landing from "./landing-page";
import Dashboard from "./dashboard";
import Analysis from "./analysis";
import { Layout } from "./Layout";
import "../style.scss";

const App = props => {
  return (
    <Router>
      <Switch>
        <Route exact path="/">
          <Layout>
            <Landing />
          </Layout>
        </Route>
        <Route path="/dashboard/:prodID/search">
          <Layout>
            <Dashboard />
          </Layout>
        </Route>
        <Redirect from="/dashboard/:prodID" to="/dashboard/:prodID/search" />
        <Route path="/dashboard/:prodID/analyze">
          <Layout>
            <Analysis />
          </Layout>
        </Route>
        <Route
          component={() => (
            <div id="error">
              <h1>404 ERROR</h1>
              <h2>Page not found</h2>
            </div>
          )}
        />
      </Switch>
    </Router>
  );
};

export default App;

This approach would help you achieve your second goal. Since the navbar is now nested under each route, you can easily fetch the params from the path and conditionally render the links, like so:

// NavBar.jsx
import React from "react";
import { NavLink, useParams } from "react-router-dom";
import { Navbar, Nav } from "react-bootstrap";
import "../style.scss";

const NavBar = () => {
  const { prodID } = useParams();

  return (
    <Navbar id="nav-bar" bg="dark" variant="dark">
      <Navbar.Brand href="/">My Project</Navbar.Brand>
      <Navbar.Collapse
        id="responsive-navbar-nav"
        className="justify-content-end"
      >
        <Nav>
          {prodID && (
            <NavLink to={`/dashboard/:${prodID}/search`}>Search</NavLink>
          )}
          {prodID && (
            <NavLink to={`/dashboard/:${prodID}/analyze`}>Analyze</NavLink>
          )}
        </Nav>
      </Navbar.Collapse>
    </Navbar>
  );
};

export default NavBar;

I haven't tested it, but it should help you with your issues.

Upvotes: 1

Related Questions