vuejs/vue-router

Throw Error "NavigationDuplicated"

tcastelly opened this issue ยท 15 comments

Version

3.1.1

Reproduction link

https://codesandbox.io/s/vue-template-i2d63

Steps to reproduce

Use $router.push to change the "page". If you are using the same page, vue-router throw an Error.

What is expected?

Just nothing, or a warning.

What is actually happening?

An Error has been thrown.


  • To my point of view it's not an error. On a previous version of 'vue-router' no error was threw: https://codesandbox.io/s/vue-template-qvrfr
  • <router-link> does not have the same behavior. There is no error even if you're pushing the current page.
posva commented

This is expected, it's part of the new promise api: before, if no callbacks were supplied to router.push, errors were only sent to the global router error handler. Now, because they return a promise, if the error is not caught, you will see a warning in the console. But the error was always there because trying to navigate to same location as the current one will fail

The behavior with router-link is consistent with how it was working before, no errors are emitted (except globally), it's catching the error by adding a noop callback:

router.push('/location', () => {})

However, the correct solution if you don't care about errors is catching the error:

router.push('/location').catch(err => {})

The last version makes more sense as the Promise api is likely to become the default and the callback version to become deprecated

Hi @posva
In your types folder you can still find the push method as void
https://github.com/vuejs/vue-router/blob/dev/types/router.d.ts

is there a plan to fix this also ?

posva commented

there are two versions of push and replace matching what actually happens

When trying to use the same I get this error, seems like typescript is looks only a the same definition because once I comment out the first
replace (location: RawLocation, onComplete?: Function, onAbort?: ErrorHandler): void;
it works.

before:
image

after:
image

Any idea why?

pushing a new URL (that is the same path as current page) but the search/hash makes the URL different, doesn't work anymore.

posva commented

@shmaram I will give it a try and push a fix during the day, thanks for letting me know!

posva commented

It's fixed now, I will publish a version during the day

@posva Why does it throw a NavigationDuplicated error when updating params, though? For instance, the router page might be named "User" and have params: { id: userId }. If you call this.$router.push({ name: 'User', params: { id: selectedUserId }}) to change the user being rendered for the current page, should that really count as a NavigationDuplicated error? This is the way it is recommended to change the data for a page, so it seems odd that getting an error is expected behavior.

posva commented

the navigation failure should only happen if the final location is the same as the current one. If it happens with different params, it's a bug so please open a new issue with a boiled down repro

imtbl commented

@posva Why does it throw a NavigationDuplicated error when updating params, though? For instance, the router page might be named "User" and have params: { id: userId }. If you call this.$router.push({ name: 'User', params: { id: selectedUserId }}) to change the user being rendered for the current page, should that really count as a NavigationDuplicated error? This is the way it is recommended to change the data for a page, so it seems odd that getting an error is expected behavior.

I can confirm this behavior using this.$router.replace(). Just replacing the query params will cause the NavigationDuplicated error.

E.g., replacing #/search?term=lorem&sort=relevance with #/search?term=lorem&sort=alphabetical via

 this.$router.replace({
  path: '/search',
  query: {
    term: 'lorem',
    sort: 'alphabetical'
  }
})

will cause the NavigationDuplicated error if not catching via

 this.$router.replace({
  path: '/search',
  query: {
    term: 'lorem',
    sort: 'alphabetical'
  }
}).catch(err => {})

It will correctly update the current URL regardless of catching the error or not though.

Is this.$router.replace() not intended to be used for this? I didn't see any other function in the docs for just replacing the query params.

Is there a shorter way the prevent the console warning than router.push('/location').catch(err => {})?
Is it possible to decorate the push method to handle errors with a noop function?

Aferz commented

@posva and what about redirecting inside a route guard?

next({ replace: true, name: 'home' }).catch(err => {})

Is this even possible? Because I'm getting this error caused by a route guard :(

posva commented

@Aferz the catch must be attached to the push/replace methods

xmsz commented

@posva Why does it throw a NavigationDuplicated error when updating params, though? For instance, the router page might be named "User" and have params: { id: userId }. If you call this.$router.push({ name: 'User', params: { id: selectedUserId }}) to change the user being rendered for the current page, should that really count as a NavigationDuplicated error? This is the way it is recommended to change the data for a page, so it seems odd that getting an error is expected behavior.

I can confirm this behavior using this.$router.replace(). Just replacing the query params will cause the NavigationDuplicated error.

E.g., replacing #/search?term=lorem&sort=relevance with #/search?term=lorem&sort=alphabetical via

 this.$router.replace({
  path: '/search',
  query: {
    term: 'lorem',
    sort: 'alphabetical'
  }
})

will cause the NavigationDuplicated error if not catching via

 this.$router.replace({
  path: '/search',
  query: {
    term: 'lorem',
    sort: 'alphabetical'
  }
}).catch(err => {})

It will correctly update the current URL regardless of catching the error or not though.

Is this.$router.replace() not intended to be used for this? I didn't see any other function in the docs for just replacing the query params.

Is this fixed?

posva commented

AFAIK the problem didn't appear when I tried. As with any other bug, a boiled reproduction is required. So please open a new issue with it if you can reproduce it. One possibility for the error to happen is to have another push somewhere else or next being called with the same location as the current one. I gave more details about it at #2881 (comment)

Locking this one to redirect to that issue