React-router: v4 withRouter match object is stale not updating

Created on 14 Mar 2017  路  13Comments  路  Source: ReactTraining/react-router

Version

v4.0.0

Test Case

Don't have time to put one together but can if absolutely necessary.

Steps to reproduce

Latest versions of react-router and react-router-redux, any subcomponent (Pure or Regular) that exclusively uses withRouter contains stale data. It never updates from the values from the original page load, even when re-rendering the components.

Expected Behavior

With [email protected] everything worked as expected. I had a component for bootstrap tabs like withRouter<IOriginalProps>(SomeTab) The active flag on the tab was set via the match.params.tab where the route looks like: `

        <ConnectedRouter history={history}>
            <div>
                <Header />
                <Sidebar />
                <Container>
                    <Switch>
                        <Route {...exact} path={Env.basePath} component={RequireAuthentication(Home)} />
                        <Route name="case" path={`${Env.basePath}/foo/:id/:tab`} component={RequireAuthentication(Foo)}/>
                    </Switch>
              </Container>
            </div>
        </ConnectedRouter>

Foo has: export default connect(mapStateToProps)(Foo) and contains the list of <SomeTab> components to render the tab within Foo.

Actual Behavior

What's interesting here is that the redux location.pathname is always correct. It is the withRouter one that is stale.

Yes I've seen all of the other issues surrounding this and yes I've read https://github.com/ReactTraining/react-router/blob/v4/packages/react-router/docs/guides/blocked-updates.md however my issue isn't that the SomeTab won't re-render, it actually re-renders every single time I click a different tab. The problem is the match is stale, it always references whatever the initial page load is, but is never updated with the new match values.

Most helpful comment

I have also come across this issue:
Match is always the below value when using react-redux Provider.

{"path":"/","url":"/","isExact":false,"params":{}}

Regardless of the actual route. In the mean time I will have to use the location prop to parse the route.

All 13 comments

Is this also an issue with a regular <Router>?

@pshrmn That's interesting, I replaced the ConnectedRouter with Router from react-router-dom and now I I don't get re-renders. I think that would link back to the blocked-updates however when I look at the React dev tools the Router component is receiving the updated location, but nothing else below it appears to be. I guess the ConnectedRouter would receive the new redux location and force re-renders. And actually, the Container I had was a PureComponent. I changed that to React.Component and now magically things work.

I think this may indicate that there is some incompatibility with [email protected]. Since react-router always recommends using withRouter, I actually think I'm going to completely remove the react-router-redux/connected-react-router stuff. In using withRouter I now can't think of a reason why I need the router in redux. I'm not saying it would be amazing to have it there, but the docs say it is unreliable. Closing this out.

Hi, did you find a solution for this? I'm experiencing the same problem.
thanks

@fgiarritiello I personally no longer use the connected-react-router and I rely on a combination of embedded Routes which end up being more powerful imo, and also in some places I use redux state to manage which tabs are open and such as I need that in other parts of my app.

I'm having the same issue here with the regular Router. Routed components get their match refreshed fine. But connected components using withRouter only get the location refreshed not their match.

I narrowed this down to connected components that are not within a Route/>. I was under the impression that as long as this was within a Router /> and used withRouter that things should work. All other properties except match seem to work fine.

It'd be nice to get confirmation if this is expected behavior or an actual bug.

I have also come across this issue:
Match is always the below value when using react-redux Provider.

{"path":"/","url":"/","isExact":false,"params":{}}

Regardless of the actual route. In the mean time I will have to use the location prop to parse the route.

I came across a similar issue this week, where location was updating, but not match. Turns out we were using and old version of react which was incompatible with the latest version of react-router. Fixed by upgrading react from 14 to 16.

I am still having this problem. props.location is updated but match isn't and I am using the last version of react-router and react.

Getting the same issue! @and3rsonsousa : Did you find a workaround ? @engineersamuel ~ Could you provide more clarification as of what you did to circumvent your problem ?

@etiennejcharles I have a top level Switch and routes for paths at the first level, like /app. Then within the App component I have an embedded Route with path /app/user, then in User I would have an embedded Route with path .../:userId. I.e. I took the route of embedding routes in specific Components so when the routes match, I have the immediate context and match right when I need it.

Hmm, I wasn't having this issue, and suddenly today for no apparent reason this started happening to me.

Here is my logic:

export default Component => compose(
  withRouter,
  connect((state, ownProps) => ({
    sections: filter(state.curriculum.sections, { CurriculumID: ownProps.match.params.CurriculumID })
  }), (dispatch, ownProps) => ({
    get: () => dispatch(getCurriculumSections(ownProps.match.params))
  })),
  lifecycle({
    componentDidMount() {
      if (!this.props.sections.length) {
        this.props.get();
      }
    },
    componentDidUpdate(pp) {
      console.log(pp.location.pathname, this.props.location.pathname);
      console.log(pp.match.params.CurriculumID, this.props.match.params.CurriculumID);
      if ((pp.match.params.CurriculumID !== this.props.match.params.CurriculumID) && !this.props.sections.length) {
        this.props.get();
      }
    }
  })
)(Component);

All it does is detect if the match changes, but I'm continually getting the initial state as the location / match object.

Well I found the problem, but I don't quite understand it:

  connect(null, { removeCurriculumSection }),
  withCurriculumSections,

In my wrappers, I had the connect(null, ....) statement ABOVE the withRouter, and as soon as I moved it below it, it started functioning correctly. Something about connect(null) was breaking things.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alexyaseen picture alexyaseen  路  3Comments

jzimmek picture jzimmek  路  3Comments

Waquo picture Waquo  路  3Comments

stnwk picture stnwk  路  3Comments

wzup picture wzup  路  3Comments