Route change updates with React-router 4

I was updating the packages in my React bootstrap/quick start project yesterday and noticed that a totally rewritten (again) version of React-router was out. Sweet! So let’s upgrade. I’d been updating packages all morning (with the help of Version Eye) so I was ready for an end-of-the-day challenge.

The new API makes much more sense to me than the old way and I really don’t need my container anymore since you can integrate the routing components directly with the app components. “More easier to reason about.” And they’ve added components to handle change animations which I’m going to get into later today.

The one part that wasn’t clear (and wasn’t available via Google or StackOverflow) was how to listen for route changes in a component. With the earlier version (1.something or other) I was using this on my navigation bar to reflect the current route:

import {browserHistory} from 'react-router';

// React stuff 

componentDidMount() {
  browserHistory.listen((event) => {
   this.updateCurrentPath(event.pathname);
  })
}

// More React stuff

I dug into the code to find a solution and discovered this neat pub/sub system they built for it called React-broadcast. You wrap the top level in a broadcaster component and then individual components nested inside are wrapped in a subscriber component to receive updates on a specific channel. Looking at StaticRouter, the parent for the browser and hash routers, the whole route shebang is wrapped in a “location” broadcaster! So you just have to wrap the necessary components in a subscriber and you’ll get updates.

I was able to modify my code above and remove the listener and integrate it directly into my render() method:

import {LocationSubscriber} from 'react-router/Broadcasts';

render() {
  return (
    <div className="navigationbar">
      <LocationSubscriber channel="location">
      {(location) =>(
        <ul>
        {
          this.props.nav.map((item, i) => {
            return <li key={i}><Link
            className={location.pathname === item.route ? 'active' : ''}
            to={item.route}>{item.label}</Link></li>
          })
        }
        </ul>
      )}
      </LocationSubscriber>
    </div>
 );
}

And it works perfectly! The only gotcha is the children of the LocationSubscriber are the result of function rather than nested, but that makes sense since you’re passing the location in.

Hope this helps someone else. Is there a better way to accomplish this? Leave a comment …

Leave a Reply