14
votes

I have a navigation component which renders each navigation link. The return of this component looks like this:

     return (
        <li key={`nav-list-${el.title}-${index}`}>
          <Link to={{pathname: '/c/' + el.urlkey}}
                activeClassName={s.active}>{el.title}</Link>
          {subNav}
        </li>
      );

The active class is only set on a page refresh. When clicking through the links the active indicator stays at the initial link.

I'm not using Redux so it doesn't seem to be related to this issue: activeClassName does not work on the sideMenu when clicking on the link

My route look like this:

      <Route path="/c/:category(/:title)" component={CategoryView} name="category" />

using React-Router 2.8.1 and browserHistory

7
had the same issue, problem was related to the fact that the component which had the activeClassName was not the component i passed to <Route /> so it was not getting updated by React. Wrapping my component inside withRouter did the trick!Arustamyan G.
Did you try using <NavLink /> instead of <Link />?Dhawal M
@ArustamyanG. For me, your solution is correct. I read the documment, reacttraining.com/react-router/web/api/withRouter I , I cann't understand why it works. can you explain more about it?MinLoveSu

7 Answers

19
votes

Instructions

  1. Use <NavLink> instead of <Link> and add exact as a property

  2. Include exact as a property to ensure activeClassName only triggers on url paths that match your location exactly

Example

<NavLink exact activeClassName="active" to="/path1">
<NavLink exact activeClassName="active" to="/path2">

Source

https://reacttraining.com/react-router/web/api/NavLink/exact-bool

exact: bool

When true, the active class/style will only be applied if the location is matched exactly.

6
votes

It's hard to debug without seeing the complete code but problems with React Router activeClassName can often be solved by either:

  • Making sure that you have one IndexLink:

    <li><IndexLink to="/" activeClassName="active">Home</IndexLink></li> <li><Link to="/" activeClassName="active">About</Link></li> <li><Link to="/" activeClassName="active">Contact</Link></li>

  • Or using the onlyActiveOnIndex attribute on all Links:

    <li><Link to="/" activeClassName="active" onlyActiveOnIndex>Home</Link></li> <li><Link to="/" activeClassName="active" onlyActiveOnIndex>About</Link></li> <li><Link to="/" activeClassName="active" onlyActiveOnIndex>Contact</Link></li>

Here is a working demo of a second solution: http://codepen.io/PiotrBerebecki/pen/qaKqRW

3
votes

Had the same issue! Resolved by wrapping the parent component via withRouter. E.g.

import { withRouter } from 'react-router';

class NavBar extends Component {
    ...
}

export default withRouter(NavBar);
2
votes

<NavLink
  to="/faq"
  activeClassName="selected"
>FAQs</NavLink>

from ReactTraining did it for me

0
votes

I didn't want to upgrade just yet as it was causing more issues than it resolved. And I wasn't using Redux so the whole connect thing didn't apply. I tried render both pure and unpure (using the pure-render decorator) with no result.

So I decided to write the link handling manually. It's super verbose I know but it works!

I added the router context:

static contextTypes = {
   router: React.PropTypes.object
};

The render returns:

return (
   <li key={`nav-${el.title}`} className={`${isActiveClass}`}>
     <a onClick={this.state.LinkClick} data-item={urlkey}>{el.title}</a>
   </li>
);

The click handler looks like this:

LinkClick(e){
   e.preventDefault();
   const elem = e.currentTarget;
   const item = elem.dataset.item;
   this.context.router.push('/c/'+item);
   this.setState({
     activeLink: item
   });
 }

Finally I set the class in the render:

  const activeLink = this.state.activeLink;
  let isActiveClass = '';
  let navLinks = map(availableCategories, (el, index) => {
  const urlkey = el.urlkey;

  // Check the active item on page render
  const isActive = this.context.router.isActive('/c/' + urlkey);

  if(activeLink === urlkey || isActive){
    isActiveClass = s.active;
  } else {
    isActiveClass = '';
  }
0
votes

There are 2 solutions for this:

Edit: you're not using Redux so the second solution won't work.

0
votes

I was having this problem in react-router-dom v4.3. and my entire App was already wrapped using withRouter. I'm also using react-redux. I noticed that it would add class to the correct link.

const mapStateToProps = (state) => {
return {
    router: state.router,
    location: state.route.location
 }
}
export default connect(mapStateToProps, mapDispatchToProps)(Header)

and my navigation links looked like this

 <NavLink
        exact
        to="/logs"
        className="menu-item"
        activeClassName="selected"
        >
        <i className="st-icon-log-reports" /> logs 
 </NavLink>