Next.js: Parameterized URLs and history state

Created on 28 Jul 2017  路  11Comments  路  Source: vercel/next.js


I have set up parameterized routing in conjunction with shallow routing in our project as per https://github.com/zeit/next.js/blob/master/examples/parameterized-routing and https://github.com/zeit/next.js/tree/master/examples/with-shallow-routing.

The router appears to be replacing history state instead of pushing history state if I push a different parameterized route for the same pathname. This results in pushing parametrized routes to appear to work until the user attempts to use the browser's back button and it skips over all state changes.

  • [x] I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior



User follows a link to /example.
Router.push is called with the route /example and asPath /example/test1 and the shallow option.
Router.push is called with the route /example and asPath /example/test2 and the shallow option.
User his the back button and the URL is set to /example/test1.

Current Behavior



User follows a link to /example.
Router.push is called with the route /example and asPath /example/test1 and the shallow option.
Router.push is called with the route /example and asPath /example/test2 and the shallow option.
User his the back button and the URL is set to /example.

Your Environment


| Tech | Version |
|---------|---------|
| next |2.4.5|
| node |6.9.1|
| OS |macOS sierra|
| browser |chrome 59|

bug

All 11 comments

I changed the shallow routing example as follows to use parameterized URLs.

diff --git a/examples/with-shallow-routing/pages/index.js b/examples/with-shallow-routing/pages/index.js
index fd9a556..a1bd96e 100644
--- a/examples/with-shallow-routing/pages/index.js
+++ b/examples/with-shallow-routing/pages/index.js
@@ -25,8 +25,10 @@ export default class Index extends React.Component {
   incrementStateCounter () {
     const { url } = this.props
     const currentCounter = url.query.counter ? parseInt(url.query.counter) : 0
-    const href = `/?counter=${currentCounter + 1}`
-    Router.push(href, href, { shallow: true })
+    const newCounter = currentCounter + 1
+    const href = `/?counter=${newCounter}`
+    const as = `/counter/${newCounter}`
+    Router.push(href, as, { shallow: true })
   }

It works as expected.

Could you send us a sample repo with your issue?
Please re-open when you've it.

@arunoda - I think the issue here is that shallow routing is being used correctly, (example was not the issue except for the href, href but we new what was meant. The issue is that when using it twice in a row, hitting back in the browser does not take you to the previous shallow route. Wanted to make sure it was clear.

Is the expected behavior @nealalpert posted correct for the design of shallow routing?

We can create a sample repo and try and reproduce the issue using version 2.4.5, but we my not be able to get to it until next week.

If you want I can pull down the shallow routing example and set it up with our use case but after some investigation we've figured out what's going awry.

Our shallow routing is using the same pathname with no query string but is showing different URLs. Example:

Router.push(`/example`, `/example/prettyurl1`, { shallow: true });
Router.push(`/example`, `/example/prettyurl2`, { shallow: true });

This results in the urlIsNew function in router.js always replacing history state instead of pushing it.

if (!this.urlIsNew(pathname, query)) {
    method = 'replaceState'
}
...
urlIsNew (pathname, query) {
    return this.pathname !== pathname || !shallowEquals(query, this.query)
}

As the pathname is always the same and the query object is aways empty isNewUrl was always returning false. We were able to circumvent this by changing the way we were routing:

Router.push(`/example?pretty=url1`, `/example/prettyurl1`, { shallow: true });
Router.push(`/example?pretty=url2`, `/example/prettyurl2`, { shallow: true });

As this will result in the query object comparison failing and the urlIsNew function to return true.

If we should offer a way to have shallowing routing also compare the presented url if an option is present in the call to Router.push please let me know @arunoda and we'd be more than happy to open a merge request for it. Otherwise maybe we should make a note in the documentation about this behavior.

Thanks. I think now we know the issue.
Actually, we need to compare the as URL in this case with this.urlIsNew.
Once we did it, it'll fix the issue.

Yeah! Send us a PR :)

Friendly ping, I ran into this bug today, the workaround trick of @nealalpert worked for me... Have you been able to put together a PR for that ?

Same issue

Same issue here and not really wanting to use shallow routing, is there an update on the PR?

The bug is indeed with urlIsNew as it compares pathname (i.e. NOT what is in browser url bar, but next.js page name), and query. So if you browse within same page replaceState is called instead of pushState. I think urlIsNew should be changed so it compares browser's url instead.

As a workaround I've created helpers around Link and push that change query params passed to next.js page, as so:

query.rand = (+new Date()).toString() 

Looks related to this issue: https://github.com/zeit/next.js/issues/4871

It is still an issue when:
router1 with url /a, asPath /a
router2 with url /a, asPath /b

  1. push router2
  2. then push router1.
    actually:
    the history is replaced by router1
    expected:
    router1 should be pushed into the history, while the asPath is different from the previous

Rout cause:

  urlIsNew (pathname, query) {
    return this.pathname !== pathname || !shallowEquals(query, this.query)
//                ^^^ as #4153 choose `as` to compare, we should use `parse(this.as, true).pathname`
  }
Was this page helpful?
0 / 5 - 0 ratings

Related issues

havefive picture havefive  路  3Comments

kenji4569 picture kenji4569  路  3Comments

irrigator picture irrigator  路  3Comments

formula349 picture formula349  路  3Comments

YarivGilad picture YarivGilad  路  3Comments