Rerendering component on route param change recalling created hooks

Vue router reuses the same component on route param change instead of rerendering the component, which means that the lifecycle hooks of the component will not be called.

So to tackle this issue I do this

  watch: {
    '$route.params.id': function (id) {
      this.loadInvoice().then((data) => {
        blablabla...
      })
    }
  },
  created: function () {
    this.loadInvoice().then((data) => {
      blablabla...
    })
  },

It works but as you see I need copy paste the same code. Is there a way I can recall the created hook on ‘$route.params.id’ change?

I tried below way, but my created hook still doesn’t run

  watch: {
    '$route.params.id': function (id) {
      this.$forceUpdate()
    }
  },

Lastly I’m aware of the :key property method. However I cannot use it since my component is loaded directly from the route like so

  {
    path: 'invoice/detail/:id',
    component: InvoiceDetailPage,
    name: 'invoice-detail',
    meta: { title: 'Financial Management'}
  },

I am able to use :key like this

<router-view :key="$route.path"></router-view>

as told here https://forum-archive.vuejs.org/topic/4840/keep-canreuse-in-vue-router-2-0 but that will cause the components for ALL the other routes to also rerender on param change.

What’s the best way to go about this? Maybe introduce a route specific canReuse option for vue router routes?

3 Likes
  1. The usual approach would be to extract the repeated code into a method:
  watch: {
    '$route.params.id': function (id) {
      this.doStuff()
    }
  },
  created: function () {
    this.doStuff()
  },
methods: {
  doStuff() {
    this.loadInvoice().then((data) => {
        blablabla...
      })
  }
}

Also, you can use the key prop:

<router-view :key="$route.name + ($route.params.id || '')">
15 Likes

Thanks for the suggestions. I decided to go with the <router-view :key="$route.name + ($route.params.id || '')"> way.

However, I suggest adding a reuse option when defining routes in vue-router in future releases. Then we can:

  {
    path: 'invoice/detail/:id',
    component: InvoiceDetailPage,
    name: 'invoice-detail',
    meta: { title: 'Financial Management'},
    **reuse: true**
  },
2 Likes

That probably won’t happen.

I like the route.id watch - hadn’t seen / thought of that before! One for the toolkit…

1 Like

Thanks i worked for me !

@LinusBorg
What if we have multiple query parameter. if all or more than 2 query param changes then doStuff will be called multiple times. I can use debounce but is their any other easier vuejs way to do this?

watch: {
        "$route.query.end": function (id) {
             this.doStuff();
        },
        "$route.query.start": function (id) {
            this.doStuff();
        },
        "$route.query.product": function (id) {
             this.doStuff();
        }
    },
1 Like

I used this exact approach and discovered a side effect (that I wasn’t aware of) of using this approach. Here is my setup:

  • All routes use Lazy Loading of the components
  • search route receives search phrase as the id
  • search component (Search.vue) uses created and watch to pass id to another method (as discussed here)
  • once a search is performed and that component is loaded, the search component watch will always be triggered, even when other routes are used and other components are loaded (Details.vue, Portfolio.vue, etc) as long as an id is passed in that route to the components
  • The other routes and components do not use a watcher
  • the issue is that the app uses this process to access an API, so I don’t want to call the API unless the route is used and the Search.vue component receives a search phrase.

Questions:

  • How do I prevent the watcher in Search.vue from running when that route isn’t used and another route/component is in use?
  • Since the URL could be bookmarked, what other alternatives to a watcher do I have to always call the method and pass the phrase (id) when the route is repeatedly called?

Thanks.

So is this search always dispplayed no matter the route?

Anyway - just check inside this.$route.name the watcher to check if you are indeed on a route that should trigger a search

Search.vue (has the watcher) is only displayed when you route to “/search/details/search phrase here”. I have a console.log(id) in the watcher code, and I noticed that when I route to any other URL ("/portfolio/" or “/customer/customerIdHere”, etc) after routing to search (lazy loads search.vue component) that the console.log(id) will run even when the search.vue component should not be in view.

Here is an example sequence:

  • Route to “/customer/12345” the console.log does NOT get called
  • Route to “/search/details/chocolate cake” the console logs “chocolate cake”
  • Route to “/customer/12345” the console.log displays “12345” even though the Search.vue with watcher is not on the screen.
  1. Does a watcher always run with every route, even if the component is not on the screen, but is in memory?

  2. Is there a better way to always catch a route id, every time one is passed to a specific route and component?

I will look into your suggestion, but wanted to clarify and get answers to the watcher question, as I did not realize it would always run, even when not in view.

Thanks!

It will run as long as the component is mounted, which means it’s on the screen or kept “alive” with a<keep-alive> component wrapping it.

So unless you do the latter, the watcher should be deactivated as soon as you naviugate away from the search route, as this will umount the Search component, which will deactivate all watchers in this component.

I didn’t even think about keep-alive, which was the issue.

Removing that, but keeping the watcher seems to have done the trick.

thanks!

It worked for me! Thank you!:smile:

I made a plugin.

Features

  • Restart component when change route params and query
  • Force restart current component