gaganbm
gaganbm

Reputation: 2843

Highlight current page in bootstrap with react

I have seen multiple questions related to this and I have tried them all, with no results. So, posting yet another seemingly duplicate question.

I am trying to highlight the current page button in the navigation bar. For simple examples, where I am not routing it to a new page, it works. But when I route it to a different page (a separate react component) it doesn't work.

Here is the code I have:

App.jsx:

class App extends Component {
  render() {
    return (
        <BrowserRouter>
            <div>
                <Route exact={true} path='/' component={HomeApp}/>
                <Route path='/form' component={SomeForm}/>
            </div>
        </BrowserRouter>
    );
  }
}

NavigationBar.jsx

class NavigationBar extends Component {

    componentDidMount() {
        toggleIcon();
    }

    render() {
        return (<div id="topheader">
        <nav className="navbar navbar-expand-lg navbar-light bg-light ">

            <a className="navbar-brand" href="#">Whatever</a>

            <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
                    aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span className="navbar-toggler-icon"></span>
            </button>

            <div className="collapse navbar-collapse" id="navbarNav">
                <ul className="navbar-nav nav nav-pills ml-auto">
                    <li className="nav-item active">
                        <a className="nav-link" href="/">Home <span className="sr-only">(current)</span></a>
                    </li>
                    <li className="nav-item">
                        <a className="nav-link" href="/form">Submit Form</a>
                    </li>
                    <li className="nav-item">
                        <a className="nav-link" href="#">Sign Up</a>
                    </li>
                    <li className="nav-item">
                        <a className="nav-link " href="#">Login</a>
                    </li>
                </ul>
            </div>
        </nav>
            </div>
        )
    }
}

export default NavigationBar

navigation.js

import $ from 'jquery';

export function toggleIcon() {
    $( '#topheader .navbar-nav a' ).on( 'click', function () {
        $( '#topheader .navbar-nav' ).find( 'li.active' ).removeClass( 'active' );
        $( this ).parent( 'li' ).addClass( 'active' );
    });
}

As seen, the highlighting works when I click Sign Up or Login, as they are not being routed to any other component.

But when I click on Submit Form which is routed to SomeForm component, the highlighting goes back to Home button.

For more details, I am posting the contents of HomeApp and SomeForm components:

HomeApp.jsx

class HomeApp extends Component {
    render() {
        return (
            <div className="container">
                <NavigationBar/>
                <Jumbotron/>
            </div>
        )
    }
}

export default HomeApp

SomeForm.jsx

class SomeForm extends Component {
    render() {
        return (<>
                <div className="container">
                    <NavigationBar>
                    </NavigationBar>
                </div>
            </>
        )
    }
}

export default SomeForm

Upvotes: 1

Views: 6873

Answers (2)

Aleksandr  Primak
Aleksandr Primak

Reputation: 111

Since you need to highlight parent element of link (li) you can use withRouter (or useLocation hook for functional components) with NavLink from react-router in NavigationBar:

import React, { Component } from 'react';
import { withRouter } from 'react-router';

class NavigationBar extends Component {

    constructor(props) {
        super(props);
        this.renderMenuLink = this.renderMenuLink.bind(this);
    }

    componentDidMount() {
        toggleIcon();
    }

    renderMenuLink({ path, label }) {
        const { location } = this.props;
        const isActive = location.pathname === path;
        return (
            <li key={path} className={`nav-item ${isActive ? 'active' : ''}`}>
                <NavLink className="nav-link" to={path}>
                     {label}{isActive ? <span className="sr-only">(current)</span> : ''}
                </NavLink>
            </li>
        );
    }

    render() {
        const menuLinks = [
            { path: '/', label: 'Home' },
            { path: '/form', label: 'Submit Form' },
            { path: '/register', label: 'Sign Up' },
            { path: '/login', label: 'Login' },
        ];
        return (<div id="topheader">
        <nav className="navbar navbar-expand-lg navbar-light bg-light ">

            <a className="navbar-brand" href="#">Whatever</a>

            <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
                    aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span className="navbar-toggler-icon"></span>
            </button>

            <div className="collapse navbar-collapse" id="navbarNav">
                <ul className="navbar-nav nav nav-pills ml-auto">
                    {menuLinks.map((menuLink) => this.renderMenuLink(menuLink))}
                </ul>
            </div>
        </nav>
            </div>
        )
    }
}

export default withRouter(NavigationBar);

Upvotes: 1

awran5
awran5

Reputation: 4536

Please follow along this structure:

import { BrowserRouter, Switch, Route } from "react-router-dom"
class App extends React.Component{
  render() {
    return (
      <BrowserRouter>
        <div className="App">
          <Navigation />
          <Switch>
            <Route exact path="/" component={Home} />
            <Route exact path="/about" component={About} />
            <Route exact path="/contact" component={MoreRoutes} />
          </Switch>
        </div>
      </BrowserRouter>
    )
  }
}

And you need to use NavLink as @Mikail Bayram mentioned, that will allow you to use activeClassName also, the exact prop is required on each of your routes to have routes render only when the path is have the exact match. Please note that you can call the active class whatever you like.

Your Navigation should look like this:

import { NavLink } from "react-router-dom"
const Navigation = () => (
  <nav>
    <ul>
      <li>
        <NavLink exact activeClassName="active" to="/">
          Home
        </NavLink>
      </li>
      <li>
        <NavLink exact activeClassName="active" to="/about">
          About
        </NavLink>
      </li>
      <li>
        <NavLink exact activeClassName="active" to="/contact">
          Contact
        </NavLink>
      </li>
    </ul>
  </nav>
)

As a side note: You should Never combining jQuery with React

Here is a live example at codeSandbox

Upvotes: 4

Related Questions