react-router: Route changes don't re-render Links

Version

4.0.0-alpha.3

Steps to reproduce

Put Link components in a location that persists across pages (like a nav bar). Use Matches to change the content area based on the current route.

Expected Behavior

Links should re-render (or at least consider re-rendering in a shouldComponentUpdate) when the location changes.

Actual Behavior

The ‘active’ statuses of the Link components do not change unless they are forced to re-render.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 19 (9 by maintainers)

Most upvoted comments

@timdorr I don’t think you should have closed this. You may not have a solution, but that doesn’t make the problem non-existant. People are going to run into this. I am upgrading from React-Router v2 to v4, and I had <Links> in my NavBar that broke for this exact reason. You are saying that can’t be fixed, but that’s a pretty good reason to leave this issue open. If a workaround comes out, great. If not, then people should know that a major, common use case that worked in V2 is unfixably broken in V4, and maybe they shouldn’t upgrade.

If you need the connected component to update, why not make the connect impure?

const Nav = connect(null, null, null, {pure: false})(
  () => (
    <ul>
      <li><Link to="/" activeClassName="active">Home</Link></li>
      <li><Link to="/about" activeClassName="active">About</Link></li>
      <li><Link to="/topics" activeClassName="active">Topics</Link></li>
    </ul>
  )
);

You could include a wrapper if you’re using it often.

function AlwaysUpdateConnect(mapState = null, mapDispatch = null, mergeProps = null, options = {}) {
  return connect(
    mapState,
    mapDispatch,
    mergeProps,
    Object.assign(options, {
      pure: false
    })
  );
}

const Nav = AlwaysUpdateConnect()(
  () => (
    <ul>
      <li><Link to="/" activeClassName="active">Home</Link></li>
      <li><Link to="/about" activeClassName="active">About</Link></li>
      <li><Link to="/topics" activeClassName="active">Topics</Link></li>
    </ul>
  )
);

This post actually helped me work around the issue:

https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/blocked-updates.md#the-solution

If anyone comes across this post - I suggest you read this, you need to pass down the location object, in order to force an update when your component is connected to redux.

I’m not sure what you’re asking me to do. It’s a quirk (not necessarily a bug) in how React handles context with sCU implemented in a parent component. There’s nothing we can do about it.

Interestingly, this just came up on Twitter: https://twitter.com/ryanflorence/status/779320581678174208

Honestly, it’s not really something we can handle. If a wrapping component wants to short circuit rendering too high up in the tree without informing its children, there’s nothing we can do to stop that on our end. This is more of an issue with React than the Router.

Incidentally, with the changes coming in react-redux, I’m thinking we can get rid of the sCU usage and clear up this mess. connect() used to have to try and outsmart the user, but now that we’re moving to reselect, it shouldn’t need to be that much of a smart ass component anymore 😄

Yep, just grab from the v4 branch.

Because parents shouldn’t need to know that much about their children. There are links scattered throughout the app, at all levels. Using React Router with Redux shouldn’t require turning off pure-render everywhere. I’ve also seen discussions about using pure-render as the default sCU for function-components, which would present the same problem.

As @timdorr mentioned above, this seems like a more fundamental issue with React’s context feature than anything else… (and it seems like a good time to point out again that it’d be really really nice to use React Router via Redux for those that want to opt into it… something like "if you put <Router> inside <Provider> and specify some option to put location into the store instead of onto context… 🙂 maybe haha)

I’ve managed to get a workaround for this. All you need to do is map the location context to props before applying connect. If you are using recompose then its easy. This will return true for sCU of connect see updated gist