-
Notifications
You must be signed in to change notification settings - Fork 546
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Redesigning Navigation failures and global handlers #150
Conversation
@posva
I say it because I think that is not very natural to check if the result of a function is undefined to check if its execution success, I think that is better to handle all with the same logic and always return a result with a context data, in the case of a failure you can include the Error with the stacktrace and in the case of a redirect you can include some information about the final route. |
Using a NavigationResult is indeed another possibility. The pattern I'm using comes from Node with error The reason I prefer using this approach is because there is no distinction in the succeeding cases: when navigation succeeds, a new |
Does this mean pushing the current route again will resolve the promise with a NavigationFailureType 'duplicated' instead of rejecting? The old behaviour encouraged adding I prefer the approach as suggested by @ducmerida to always return a NavigationResult. |
Yes.
That's not really true. A NavigationResult would be redundant as it would contain the previous location and current location: const from = {...useRoute()} // create a copy of the current route
await router.push('/...')
const currentLocation = useRoute() You still need extra logic to differentiate a failure from a success whereas with only failures, you can simply do: const failed = await router.push('/...')
if (failed) {
// handle failure
} else {
// handle success
} compared to const result = await router.push('/...')
if (result instanceof Error) {
// handle failure
} else {
// handle success
} It's also important to note that the most common use case is waiting for the navigation to be finished without failure, so making that path easy to check is valuable: if (!await router.push('/...')) // after navigation compared to if (!((await router.push('/...') instanceof Error)) // after navigation
// which would probably be written as
const result = await router.push('/..')
if (result) // after navigation Failures are usually useful for debugging and tracing back what blocked the navigation, that's why them being Error instances is so useful
That depends on how a NavigationResult would be implemented |
A problem I've found is there is no way to achieve certain functionality with vue router when writing a universal web app without throwing an error. However throwing errors now causes unhandled rejections for push and replace. Here are a couple use cases:
My question about all of these is how do I achieve the same thing without throwing an error? |
@trainiac that seems to be a case of redirection and you can handle it by checking the property |
@posva Thanks for responding. Forgive me if I'm missing something but I don't see how this answers my question. How can one tell from |
Deciding of its a 301 or 302 is up to you. From the router perspective it’s just a redirection |
BREAKING CHANGE: This follows the RFC at vuejs/rfcs#150 Summary: `router.afterEach` and `router.onError` are now the global equivalent of `router.push`/`router.replace` as well as navigation through the interface (`history.go()`). A navigation only rejects if there was an unexpected error. A navigation failure will still resolve the promise returned by `router.push` and be exposed as the resolved value.
This RFC is now in final comments stage. An RFC in final comments stage means that: The core team has reviewed the feedback and reached consensus about the general direction of the RFC and believes that this RFC is a worthwhile addition to the framework. |
Proposal seems good to me (have run into both NavigationDuplicated and "Promise rejected with undefined" errors recently after updating vue-router). I see that this is targeted for v4 - is there a chance this can instead be released with a minor version update of v3 (as such an update introduced the issues with promises in the first place)? would like to adopt this in my company's app ASAP and would prefer not to worry about larger breaking changes in v3 -> v4. |
@snoozbuster It's great you are supporting this change but because it is a breaking change, it can only be released as a major bump |
@posva all well and good, but where was that mentality when promisifying router.push and router.replace was pushed as a minor bump? that was also a breaking change - I find it hard to believe that the solution for a breaking change introduced to consumers of this library by a minor bump is to absorb even more breaking changes caused by a major bump. What is the path to resolution for this issue for consumers which cannot upgrade to v4 (which unless I am mistaken, also requires an upgrade to vue 3.0)? |
@snoozbuster It wasn't a breaking change as described at vuejs/vue-router#2881 (comment). There are temporary solutions to prevent Promise rejections for navigation failures and the |
@posva ahh, I had seen that issue but only got as far as the link to this RFC, not all the way up to that comment, so I was expecting this RFC to resolve the issue. I don't strictly agree with the statement that "it wasn't a breaking change" because I was neither returning nor awaiting All of our duplicate navigation issues are tied to one terribly janky piece of code anyway (and seeing all these errors made me realize it's even more janky than I thought it was), so I will handle them there for now and wait in anticipation for this new API change. Thanks for explaining - feel free to mark these last couple comments as off-topic. |
|
||
# Adoption strategy | ||
|
||
- Expose `NavigationFailureType` in vue-router@3 so that Navigation Failures can be told apart from regular Errors. We could also expose a function `isNavigationFailure` to tell them apart. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahh, this will help too. is there a better way to check for navigation failures right now than e && e.name === 'NavigationDuplicated'
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no documented way to check for duplicated navigations right now (it's private api)
Co-Authored-By: Nicolas Turlais <[email protected]> Co-Authored-By: Patrick <[email protected]>
Co-Authored-By: Alex Van Liew <[email protected]>
d98b674
to
13279a8
Compare
@posva import { NavigationFailureType, isNavigationFailure } from 'vue-router'
router.push('/').then((failure) => {
if (failure) {
// Having an Error instance allows us to have a Stacktrace and trace back
// where the navigation was cancelled. This will, in many cases, lead to a
// Navigation Guard and the corresponding `next()` call that cancelled the
// navigation
failure instanceof Error // true
if (isNavigationFailure(failure, NavigationFailureType.canceled)) {
// ...
}
}
}) But, in router's doc, example is this: const { isNavigationFailure, NavigationFailureType } = VueRouter
// trying to access the admin page
router.push('/admin').catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.redirected)) {
// show a small notification to the user
showToast('Login in order to access the admin panel')
}
}) (link: https://router.vuejs.org/guide/advanced/navigation-failures.html#detecting-navigation-failures) I confuse that one is then and another is catch which capture the failure, so which is right place to get failure? |
The catch is for Vue Router 3. I fixed the typo on vue router 4 docs |
In Vue 3, how can one navigate to the same route, but with different query parameters? Thanks |
@mariusa I think the navigation is done. Your component data/derived data, should be reactive maybe 🤔 it should change when the query changes 👀 https://codesandbox.io/s/summer-rgb-zo4him?file=/src/App.vue |
You're right, I've added a watch on |
Rendered