Highlight current page in bootstrap with react - reactjs

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

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

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);

Related

Adding Active class to react sidebar

I am trying to add 'active' as className to my each in sidebar component. What I want is that whichever gets activated, it should attach class 'active' to it and then set CSS accordingly.
I tried using react-router location props referring to this SO answer: https://stackoverflow.com/a/42766792/11349591, but unable to follow the syntax/result properly.
Here is my code for Sidebar.js
import React, {Component} from 'react'
import { NavLink, Link } from 'react-router-dom'
import '../../css/active.css';
export default function SideBar(){
const { location } = this.props;
const dashboardClass = location.pathname === "/" ? "active" : "";
const userClass = location.pathname.match(/^\/user/) ? "active" : "";
const listClass = location.pathname.match(/^\/list/) ? "active" : "";
return (
<div style={{flexBasis: '200px', backgroundColor: 'gray'}}>
<nav style={{textAlign:'left'}}>
<ul className="side-bar">
<li className={dashboardClass}>
<i class="fa fa-pie-chart fa-2x" aria-hidden="true" style={{color:'#ccc'}}></i>
<Link to="/dashboard">
Dashboard
</Link>
</li>
<li className={userClass}>
<i class="fa fa-user-circle fa-2x" aria-hidden="true" style={{color:'#ccc'}}></i>
<Link to="/user" >User</Link>
</li>
<li className={listClass}>
<i class="fa fa-list-alt fa-2x" aria-hidden="true" style={{color:'#ccc'}}></i>
<Link to="/list">Table List</Link>
</li>
</ul>
</nav>
</div>
)
}
App.js (Home component render Sidebar and Dashboard component)
function App() {
return (
<div className="App">
<Switch>
<Route exact path="/" component={Home} />
<Route path="/register" component={Register} />
<Route path="/login" component={Login} />
</Switch>
</div>
);
}
Home.js
export default class Home extends Component {
render() {
return (
<div style={{display: 'flex', flexDirection: 'row'}}>
<SideBar/>
<Dashboard/>
</div>
)
}
}
My console report this problem: TypeError: Cannot read property 'props' of undefined
You are trying to access this.props from a functional component and that's only possible in class based components. Change your code to this:
export default function SideBar({ location }){
// Rest of the code
You also need to pass the router props down from Home to Sidebar component:
export default class Home extends Component {
render() {
return (
<div style={{display: 'flex', flexDirection: 'row'}}>
<SideBar {...this.props}/> // Pass the props down
<Dashboard/>
</div>
)
}
}
To activate the Nav link you should use NavLink instead of Link. NavLink has one property called activeClassName, you should apply that class to it. let say if you have class called active which you want to apply after clicking on that then you can do this.
<NavLink to="/user" activeClassName={classes.active}>
<li className={dashboardClass}>
<i class="fa fa-pie-chart fa-2x" aria-hidden="true" style={{color:'#ccc'}}></i>
</li>
</NavLink>

React context consumer was rendered with multiple children error

I simply try to use react provider but somehow i get this error:
index.js:1437 Warning: A context consumer was rendered with multiple children, or a child that isn't a function. A context consumer expects a single child that is a function. If you did pass a function, make sure there is no trailing or leading whitespace around it.
this is context.js
import React, { Component } from 'react'
const Context=React.createContext()
class Providerr extends Component {
state={display:false}
displayeditor=()=>{this.setState({display:!this.state.display}) }
render() { return (
<Context.Provider value={{...this.state,displayeditor:this.displayeditor }}>
{this.props.children}
</Context.Provider>
)
}
}
const Consumer=Context.Consumer
export {Providerr,Consumer}
I want to Consume it below
import React, { Component } from 'react'
import {NavLink} from "react-router-dom"
import "./leagues.scss"
import {Consumer} from "./context.js"
export default class Navbar extends Component {
render() {
return (
<nav className="navbar navbar-expand-lg">
<a className="navbar-brand" href="#Home">
<img src="./images/customLogo.jpg" className="navlogo"/>
</a>
<div className="collapse navbar-collapse">
<ul className="navbar-nav mx-5">
<NavLink to="/">
<li className="nav-item active mr-5">
<div className="nav-link">
<span>Home</span>
</div>
</li>
</NavLink>
<Consumer> {value =>
<li onClick={value.displayeditor} className="nav-item mr-5">
<div className="nav-link modal_lig" href="">
<span>Leagues</span>
</div>
</li> }
</Consumer>
</nav>
)}}
Wrap the whole code inside Consumer as shown below. Also note that some of your html elements are not properly closed.
import React, { Component } from 'react'
import {NavLink} from "react-router-dom"
import "./leagues.scss"
import {Consumer} from "./context.js"
export default class Navbar extends Component {
render(){
return(
<Consumer>
{value => (
<nav className="navbar navbar-expand-lg">
<a className="navbar-brand" href="#Home">
<img src="./images/customLogo.jpg" className="navlogo"/>
</a>
<div className="collapse navbar-collapse">
<ul className="navbar-nav mx-5">
<NavLink to="/">
<li className="nav-item active mr-5">
<div className="nav-link">
<span>Home</span>
</div>
</li>
</NavLink>
<li onClick={()=>value.displayeditor()} className="nav-item mr-5">
<div className="nav-link modal_lig" href="">
<span>Leagues</span>
</div>
</li>
</ul>
</div>
</nav>
)}
</Consumer>
)
}
}

Calling function in App.js from another component

I'm new to ReactJS and want to use this library: https://github.com/teodosii/react-notifications-component
It states that "You must place ReactNotificationsComponent component at the root level of the application in order to work properly, otherwise it might conflict with other DOM elements due to the positioning."
I followed the usage guide, but I'm not sure how to call the addNotification function from a child component.
App.js
import React, { Component } from 'react'
import ReactNotification from "react-notifications-component"
import Header from './components/Header'
class App extends Component {
constructor() {
super()
this.addNotification = this.addNotification.bind(this)
this.notificationDOMRef = React.createRef()
}
addNotification() {
this.notificationDOMRef.current.addNotification({
title: "Awesomeness",
message: "Awesome Notifications!",
type: "success",
insert: "top",
container: "top-right",
animationIn: ["animated", "fadeIn"],
animationOut: ["animated", "fadeOut"],
dismiss: { duration: 5000 },
dismissable: { click: true }
})
}
render() {
return (
<div>
<ReactNotification ref={this.notificationDOMRef} />
<Header />
</div>
)
}
}
export default App
Header.js
import React, { Component } from 'react'
import logo from "../assets/images/logo/logo-light.png"
import { BrowserRouter as Router, Route, NavLink, Switch } from "react-router-dom"
import Home from '../pages/Home'
import Apply from '../pages/Apply'
export default class Header extends Component {
render() {
return (
<Router>
<div>
<Route render={({ location }) => (
<header id="navbar-spy" className="header header-1 header-dark header-fixed">
<nav id="primary-menu" className="navbar navbar-fixed-top">
<div className="container">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse-1" aria-expanded="false">
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
</button>
<img className="logo-light" src={logo} alt="Logo" draggable="false" />
</div>
<div className="collapse navbar-collapse pull-right" id="navbar-collapse-1">
<ul className="nav navbar-nav nav-pos-right navbar-left">
<li className="">
<NavLink to="/" className="menu-item">Home</NavLink>
</li>
<li className="">
<NavLink to="/apply" className="menu-item">Apply</NavLink>
</li>
</ul>
</div>
</div>
</nav>
</header>
)}
/>
<Route path="/" exact component={Home} />
<Route path="/apply" exact component={Apply} />
</div>
</Router>
)
}
}
When you click the "Apply" link, it loads the Apply component. In that component, they can receive an error/success notification. From that component is where I want to be able to tell the ReactNotification in App.js what title, message, and type to render. How can I communicate from Apply.js to App.js?
You can pass the addNotification function to your Header component:
<Header addNotification={this.addNotification} />
And you can continue to pass it down to other child components, until you are ready to call it.

shouldComponentUpdate() returns true but NavLink still doesn't work

I recently tried implementing a navlink like so:
import React from 'react';
import { Link, NavLink } from 'react-router-dom';
import '../css/navbar.css';
import logoWhite from '../../common/images';
const Navbar = props => {
return (
<div id="navbar" className="grid">
<Link to="/" className="column logo-container">
<img src={logoWhite} alt="Test logo" />
</Link>
<ul className="column navigation">
<li>
<NavLink to="/" exact className="nav-link" activeClassName="active">Home</NavLink>
</li>
<li>
<NavLink to="/match" className="nav-link" activeClassName="active">Partner Proposals</NavLink>
</li>
</ul>
</div>
);
};
export default Navbar;
Upon running I encountered the issue of the classname not being passed to the navlink when the route changed. Reading on articles and it led me to suspect that the shouldComponentUpdate method was being rendered false. So, I rewrote the Navbar like so:
import React from 'react';
import { Link, NavLink } from 'react-router-dom';
import '../css/navbar.css';
import logoWhite from '../../common/images';
class Navbar extends React.Component {
shouldComponentUpdate() {
console.log('Console log activated');
return true;
}
render() {
return (
<div id="navbar" className="grid">
<Link to="/" className="column logo-container">
<img src={logoWhite} alt="Test image" />
</Link>
<ul className="column navigation">
<li>
<NavLink to="/" exact activeClassName="active" className="nav-link">Home</NavLink>
</li>
<li>
<NavLink to="/match" activeClassName="active" className="nav-link">Partner Proposals</NavLink>
</li>
</ul>
</div>
);
}
}
export default Navbar;
What's strange is that the componentDidUpdate returned the value in the console (suggesting that it returned true), but the NavLink was still not passing the activeClassName
Can anyone suggest a solution to this problem?
Based on the suggestion from #mindaJalaj, I passed a pathname prop (which was rendering and re-rendering whenever there was a update to the route from the parent component), and I implemented the Navbar as follows
import React from 'react';
import PropTypes from 'prop-types';
import { Link, NavLink } from 'react-router-dom';
import { Icon } from 'semantic-ui-react';
import '../css/navbar.css';
import logo from '../../common/images/logo.png';
import navLinks from '../navLinks';
const Navbar = props => {
function renderNavLinks() {
return navLinks.map((navLink, index) => {
const key: string = `nav-link-${navLink.path}-${index}`;
const exact: boolean = navLink.exact || false;
return (
<li key={key}>
<NavLink
to={navLink.path}
exact={exact}
className="nav-link"
isActive={() => navLink.path === props.pathname}
activeClassName="active"
>
<Icon name={navLink.icon} />
<span>{navLink.title}</span>
</NavLink>
</li>
);
});
}
return (
<div id="navbar" className="grid">
<Link to="/" className="column logo-container">
<img src={logo} alt="Test logo" />
</Link>
<ul className="column navigation">{renderNavLinks()}</ul>
</div>
);
};
Navbar.propTypes = {
pathname: PropTypes.string
};
Navbar.defaultProps = {
pathname: '/'
};
export default Navbar;

React Router v4 setting activeClass on parent

Not too familiar with react router, but I need the functionality of the NavLink to set the active class on the parent li element, and not the a element.
To implement this I just looked at the source code of the NavLink and copied it to a new element. (Example using typescript, but just about the same as js anyway)
import * as React from 'react';
import { Link, withRouter, Route } from 'react-router-dom';
class LiNavLink extends React.Component<any, {}> {
render() {
const {to,exact, strict, activeClassName, className, activeStyle, style, isActive: getIsActive, ...rest } = this.props;
return (
<Route
path={typeof to === 'object' ? to.pathname : to}
exact={exact}
strict={strict}
children={({ location, match }) => {
const isActive = !!(getIsActive ? getIsActive(match, location) : match)
return (
<li
className={isActive ? [activeClassName, className].join(' ') : className}
style={isActive ? { ...style, ...activeStyle } : style}>
<Link
to={to}
{...rest}
/>
</li>
)
}}
/>
);
}
}
export default LiNavLink;
Then the usage:
<ul>
<LiNavLink activeClassName='active' exact={true} strict to="/example"><span>Active</span></LiNavLink>
<LiNavLink activeClassName='active' exact={true} strict to="/example/archived"><span>Archived</span></LiNavLink>
</ul>
I'm using a HashRouter and for some reason which I can't figure out, this does not update when the route changes, only when I hard 'refresh' the page does it update how it should.
I believe it is never updating because the props never change? So it doesn't know to update itself?
How can I get this to update? Or is my problem somewhere else?
In v4 after lots of tries I did.
Here my working code.
import React, { Component } from "react";
import logo from "../../logo.svg";
import { Link, withRouter } from "react-router-dom";
import PropTypes from "prop-types";
class Navbar extends Component {
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
};
state = {};
getNavLinkClass = path => {
return this.props.location.pathname === path
? "nav-item active"
: "nav-item";
};
render() {
return (
<nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<Link className="navbar-brand" to="/">
<img
src={logo}
width="30"
height="30"
className="d-inline-block align-top"
alt=""
/>
Utility
</Link>
<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" />
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
<li className={this.getNavLinkClass("/")}>
<Link className="nav-link" to="/">
Home
</Link>
</li>
<li className={this.getNavLinkClass("/age-counter")}>
<Link className="nav-link" to="/age-counter">
Age Counter
</Link>
</li>
</ul>
</div>
</nav>
);
}
}
export default withRouter(Navbar);
Demo working Code Sandbox
Check this one
class LiNavLink extends React.Component<NavLinkProps> {
render() {
return (
<Route exact={this.props.exact} path={this.props.to.toString()}>
{
({ match }) =>
<li className={match ? 'active' : undefined}>
<Link to={this.props.to} >
{this.props.children}
</Link>
</li>
}
</Route>
);
}
}
I am just starting with the react, so not sure if this is the best practices, but after going through router v4 docs, I used withRouter props -> location.pathname and compared it to my route.
Here is the Navigation.js:
import React from 'react';
import { withRouter } from 'react-router-dom';
import NavLink from '../General/NavLink';
const activeClass = (path, link) => {
if (path === link) {
return true;
}
return false;
};
const Navigation = props => {
const { location } = props;
return (
<ul className="menu menu--main nano-content">
<NavLink
to="/"
parentClass={
activeClass(location.pathname, '/')
? 'menu__item menu__item--active'
: 'menu__item'
}
linkClass="menu__link effect effect--waves"
>
Dashboard
</NavLink>
<NavLink
to="/users"
parentClass={
activeClass(location.pathname, '/users')
? 'menu__item menu__item--active'
: 'menu__item'
}
linkClass="menu__link effect effect--waves"
>
Users
</NavLink>
<NavLink
to="/projects"
parentClass={
activeClass(location.pathname, '/projects')
? 'menu__item menu__item--active'
: 'menu__item'
}
linkClass="menu__link effect effect--waves"
>
Projects
</NavLink>
<NavLink
href="http://google.com"
parentClass="menu__item"
linkClass="menu__link effect effect--waves"
>
Google
</NavLink>
</ul>
);
};
export default withRouter(Navigation);
From there you have parent and child classes that you can use on child component.
I found that by using CSS you can make the active link expand to fill up it's parent <li> element by setting display:block; in the active class.
For example if our link was:
<li>
<NavLink to="/overview" className=styles.sideLink activeClassName=styles.sideLinkSelected>
Overview
</NavLink>
</li>
then our CSS would be:
&__sideLinkSelected
{
background-color: blue;
display:block;
}

Resources