React Router: access match, location and other stuff from a component which is not in a Route

The problem was pretty simple. I had a BrowserRouter component with different Routes with the Navigation to be in common for all of them.

class Navigation extends React.Component {
  render() {
    return (
      <ul>
        <li>
          <NavLink to="page-one">Page One</NavLink>
        </li>
        <li>
          <NavLink to="page-two">Page Two</NavLink>
        </li>
      </ul>
    );
  }
}

class App extends React.Component {
  render() {

    return (
      <BrowserRouter>
        <div>
          <Navigation />
          <div>
            {this.props.children}
          </div>
          <Route path="/page-one" component={PageOne}></Route>
          <Route path="/page-two" component={PageTwo}></Route>
        </div>
      </BrowserRouter>
    );
  }
}

So, this is one of the menu entries and I wanted to add an active class on the li element rather then on the NavLink (which is the default behavior).

<li>
  <NavLink to="page-one">Page One</NavLink>
</li>

Since my Navigation is not inside a Ruote, it will not know by default what is the current matched route.

The best solution I’ve found, is to make use of the withRouter function, which takes a component and return a new component that will be re-rendered every time the route changes, passing different information in the props. This is exactly what I needed 🙂

import { withRouter } from 'react-router-dom';

class Navigation extends React.Component {
  render() {
    const { location: { pathname } } = this.props;

    return (
      <ul>
        <li className={pathname.startsWith('/page-one') ? 'active' : ''}>
          <NavLink to="page-one">Page One</NavLink>
        </li>
        <li className={pathname.startsWith('/page-two') ? 'active' : ''}>
          <NavLink to="page-two">Page Two</NavLink>
        </li>
      </ul>
    );
  }
}

class App extends React.Component {
  render() {
    const NavigationWithRouter = withRouter(Navigation);

    return (
      <BrowserRouter>
        <div>
          <NavigationWithRouter />
          <div>
            {this.props.children}
          </div>
          <Route path="/page-one" component={PageOne}></Route>
          <Route path="/page-two" component={PageTwo}></Route>
        </div>
      </BrowserRouter>
    );
  }
}

Further things you have injected in the props other than location are match and history.

Last note: the location you get injected has the basename stripped out, which is probably what you want anyway.