Next.js: withRouter not getting current pathname

Created on 27 Jul 2018  路  15Comments  路  Source: vercel/next.js

[ BUG ] withRouter not getting current pathname

Since version 6.1 router.pathname gets only the first loaded pathname.

To Reproduce

import Router, { withRouter } from 'next/router'
...
const ActiveLink = (props) => {

    const active = (props.router.pathname === active_url) ? ' active' : ''  

    const onClickHandler = (e) => {        
         ...
    }

    return (        
        <a href={props.href} className={'nav-link'+active} onClick={onClickHandler}>
          {props.children}
        </a>
    )
}

export default withRouter(ActiveLink)

Expected behavior

When the component re-render, it should give the current pathname.

System information

  • Version of Next.js: [e.g. 6.1.1]

Most helpful comment

I was about to open a similar issue, but while creating a reproducible example I figured out, I had code like this:

import { connect } from 'react-redux';
const Broken = connect()(withRouter(props => r.div('test')));

Which, as far as I understand, resulted in contextTypes required by withRouter being replaced with ones required by connect from react-redux. If you swap the order of connect and withRouter like this: withRouter(connect()(...)) you get the same faulty behaviour.

Might consider this to be a bug in both next.js and react-redux as both HOC's are not nice enough to combine contextProps with their first-order component arguments. New react context API could have helped here too.

Workaround: don't compose connect and withRouter directly:

const Broken_ = connect()(props => r.div('test'));
const Broken = withRouter(props => r(Broken_, props));

Screencast from a running demo https://github.com/futpib/next.js-withRouter-issue-demo:

peek 2018-10-14 12-39

EDIT: I think I'm still missing something, as applying the workaround above with connect and withRouter swapped does not work, for why that may be I don't have a clue.

All 15 comments

Please provide a full reproduction in a github repository so I can check it out quicker 馃憤

I'm not seeing query information withRouter as well

I'm not seeing query information withRouter as well

@larron Please always provide a reproduction. Otherwise there's nothing we can do to fix an issue you're having if there is something that needs to be fixed.

@timneutkens Please ignore that last comment, I found another ticket in which it was explained that this is the expected behavior in a SPA. Though I'd be curious as to why.

A link to the other issue would be helpful to get any serious reply.

I'm going to close this issue as there's no reproduction.

I was about to open a similar issue, but while creating a reproducible example I figured out, I had code like this:

import { connect } from 'react-redux';
const Broken = connect()(withRouter(props => r.div('test')));

Which, as far as I understand, resulted in contextTypes required by withRouter being replaced with ones required by connect from react-redux. If you swap the order of connect and withRouter like this: withRouter(connect()(...)) you get the same faulty behaviour.

Might consider this to be a bug in both next.js and react-redux as both HOC's are not nice enough to combine contextProps with their first-order component arguments. New react context API could have helped here too.

Workaround: don't compose connect and withRouter directly:

const Broken_ = connect()(props => r.div('test'));
const Broken = withRouter(props => r(Broken_, props));

Screencast from a running demo https://github.com/futpib/next.js-withRouter-issue-demo:

peek 2018-10-14 12-39

EDIT: I think I'm still missing something, as applying the workaround above with connect and withRouter swapped does not work, for why that may be I don't have a clue.

Solution

connect makes its wrapped component pure, preventing withRouter, placed no matter how deep in the component's tree, from getting context updates. So in order to make your ActiveLink work, pass {pure: false} as a fourth argument to your connect function like this:

connect(mapStateToProps, mapDispatchToProps, null, {pure: false})

Should anyone still have this issue, another workaround is to wrap any component in the tree with withRouter.

So if I have a connected Header containing a connected Navbar I can use the router with no issues this way:

export default withRouter(connect(mapStateToProps)(Header))

and

export default withRouter(connect(mapStateToProps)((Navbar)))

hope this helps

@timneutkens Can this be reopened now as I provided a reproducible example above?

Solution

connect makes its wrapped component pure, preventing withRouter, placed no matter how deep in the component's tree, from getting context updates. So in order to make your ActiveLink work, pass {pure: false} as a fourth argument to your connect function like this:

connect(mapStateToProps, mapDispatchToProps, null, {pure: false})

@shanvl This doesn't work well, because if you use pure: false, connect will render the child component 5-6 times instead of a single time. Terrible performance waste. Read the docs here.

@srosset81 I don't think there will be any noticeable performance issues, because rendering a "link" component is usually a very cheap operation due to its small child components subtree

@timneutkens please re-open.

I believe this is a pretty big error since an example use case does not work as documented

@timneutkens Any news on this issue ?

I don't understand why you'd ask to re-open a closed issue from mid 2018 (1,5 years old by now).

It's generally better to create a new issue:

  1. Create a new issue
  2. Follow the issue template
  3. Provide a full clear and concise reproduction
  4. We'll try to have a look at your issue

It's unlikely your issue is the same as a 1,5 year old issue that didn't have a reproduction provided.

Posting "any news" on a GitHub issue won't get your issue solved, always provide a full reproduction that is clear and concise. If not you can use GitHub emoji reactions on the initial issue.

Was this page helpful?
0 / 5 - 0 ratings