vuetify: [Bug Report][3.1.14] Components built with @/composables/router can crash when routes are missing required params

Environment

Vuetify Version: 3.1.14 Vue Version: 3.2.47 Browsers: Chrome 112.0.0.0 OS: Mac OS 10.15.7

Steps to reproduce

This is a really dirty reproduction. Clone, and npm i and run. There are two buttons, click on “VBtn Flaw” and then click on the home button in the top right corner.

Expected Behavior

Click on “Router Link” and then click on the home button on the top right corner. And see the expected behavior.

I expect that when you use a named route which is a child to a parent(s) with param(s) that it will inherit the params implicitly from it’s parent, you shouldn’t need to declare the params that are missing.

Actual Behavior

VBtn in the example crashes when you navigate back to home.

Reproduction Link

https://github.com/MatthewAry/vuetify-route-link-bug

Other comments

This reproduction shows the behavior with RouterLink and with VBtn the to params on the elements shown on those pages are basically the same. I think behaviorally when it comes to routing, there should be no difference between RouterLink and VBtn or any other component that uses @/composables/router

Update: https://github.com/vuejs/router/issues/845 has to do with this exact problem however the issue was closed because the team believed it was resolved. If you build your application in production mode you won’t see this issue. See: https://github.com/vuejs/router/issues/845#issuecomment-806762085 Which suggests that this issue may be upstream or that the useLink method in @/composables/router needs to change in some manner to be more similar to what the RouterLink component is doing to avoid this issue. 🤷

Update: See https://github.com/vuetifyjs/vuetify/issues/17176#issuecomment-1517843304

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 2
  • Comments: 19 (11 by maintainers)

Most upvoted comments

So after quite a bit of investigation this is what I’ve learned. Components in Vuetify that make use of the useLink function from Vue Router’s RouterLink component, utilize a few ComputedRef properties that useLink returns which are bound to the router’s current state. The reactive properties used are:

  • route the current RouteLocation & { href: string }
  • isActive tells us if the specified route for the link is included in the list of matched routes for the current, “visiting” route
  • isExactActive tells us if the current route being visited is the exact same route as the one specified for the link
  • href the path to the route specified for the router link. This is extracted from the same route object detailed above.

Vuetify’s useLink composable function utilizes the reactive properties listed above (with the exception of href, it gets that from route instead of using the one returned from Vue Router’s own useLink function) to turn it’s own components into proper links using <a> tags. This functionally similar in many ways to what Vue Router does when you use its RouterLink component. However, for some reason the behavior is different.

What I am seeing is that every time the current route changes, Vuetify’s router enabled components get an update, causing it to re-resolve the route for every link. And when the new route is missing params required for the links’ named route, the resolution of the href property fails causing the crash. You would expect components to unmount gracefully when you leave a route that loads and renders router link components using named routes which require params, what happens is that this route re-resolve operation takes place BEFORE the components get unmounted. This is basically a race condition.

I have come up with a workaround, but it should be considered a hack and not a solution. This hack works for onRouteEnter but not for when your params change onRouteUpdate, so you’ll need to figure out how to handle onRouteUpdate because that will require more considerations depending on your use case.

In the parent component, you need to obtain the current router params and store them in a const.

const routeParams = { ...useRoute().params };

routeParams will end up being assigned the route parameters that were present the moment the parent component was rendered and will be a non-reactive object.

Then in your template you will want to do something like this.

<VBtn :to="{ name: 'RouteName', params: routeParams }">Your Router Link Button</VBtn>

This way you’ll be passing in the necessary params to make your link properly resolve even if your route changes to something that doesn’t contain the necessary params.

Hello,

we had the same issue and we resolved it by upgrading some dependencies (with yarn upgrade-interactive) :

  • vueuse/core, 10.5.0 ❯ 10.7.2
  • vue-router, 4.2.4 ❯ 4.2.5
  • vue-tsc, 1.8.4 ❯ 1.8.27
  • vuetify, 3.4.9 ❯ 3.4.11

I was able to do this and your patch resolves the issue in my codebase also. Thanks.