Skip to content

Undocumented Regression in beforeRouteLeave Guard When next() is Used #901

@amxmln

Description

@amxmln

Version

4.0.6

Reproduction link

https://github.com/amxmln/vue-router-next-issue-reproduction

Steps to reproduce

  • Navigate to the About-route
  • Try to navigate away from the about route with the navigation menu, the button, or the browser back button
  • The beforeRouteLeave guard will kick in and show a toast, but not navigate away
  • Clicking the "Discard and Leave" button will actually perform the navigation, clicking close only gets rid of the "toast"
  • Clicking "Go back" multiple times will eventually unload the app since the navigation history is updated, despite next() not being called

What is expected?

The toast shows up, but the URL stays the same until the "Discard and Leave" button is clicked (and thus next() is called).

What is actually happening?

The URL (but not the router-view) changes, the navigation history gets updated. Closing the toast does not restore the current route.


In a Vue2 app with vue-router@3, I used to prevent navigating away when there were unsaved changes with a beforeRouteLeave-guard similar to this one:

beforeRouteLeave(to, from, next) {
    if (this.forceNavigation)  next();
    else if (this.unsavedChanges) {
      this.$store.commit('addToast', {
        action: next,
        actionLabel: 'Discard Changes',
        message: 'You have unsaved changes, do you want to discard them?',
        type: 'warning',
      });
    } else next();
  },

This successfully prevented the navigation until the button in the toast was clicked. Ignoring or closing the toast would do nothing (as expected). Even when clicking a navigation link multiple times, this would only result in more toasts being created, but no navigation (again, as expected).

This construct does no longer work in Vue3 with vue-router@4. While trying to rewrite the code to use a promise instead (and awaiting that one), I noticed that the URL changes while the promise is still pending and only changes back once false is returned in the guard. I don’t think that’s what users are expecting and it feels like a bug, which is why I’m reporting it—in case it’s not a bug, but a conscious choice, I think that should probably be mentioned in the Migration Guide somewhere, since there’s a bunch of SO and Vue Forum threads proposing similar solutions for Vue2.

Thank you for your hard work!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions