Search code examples
vue.jsvue-routervuejs3

How to stop Vue.js 3 watch() API triggering on exit


I have implemented a watch within a Vue component that displays product information. The watch watches the route object of vue-router for a ProductID param to change. When it changes, I want to go get the product details from the back-end API.

To watch the route, I do this in Product.vue:

import { useRoute } from 'vue-router'

    export default {
      setup() {
        const route = useRoute();

        async function getProduct(ProductID) {
          await axios.get(`/api/product/${ProductID}`).then(..do something here)
        }
    
        // fetch the product information when params change
        watch(() => route.params.ProductID, async (newID, oldID) => {
            await getProduct(newId)
          },
          //watch options
          {
          deep: true,
          immediate: true
          }
        )
      },
    }

The above code works, except that if a user navigates away from Product.vue, for example using the back button to go back to the homepage, the watch is triggered again and tries to make a call to the API using undefined as the ProductID (becaues ProductID param does not exist on the homepage route) e.g. http://localhost:8080/api/product/undefined. This causes an error to be thrown in the app.

  1. Why does the watch trigger when a user has navigated away from Product.vue?
  2. How can this be prevented properly? I can do it using if(newID) { await getProduct(newId) } but it seems counterintuitive to what the watch should be doing anyway.

UPDATE & SOLUTION Place the following at the top replacing the name for whatever your route is called:

if (route.name !== "YourRouteName") {
   return;
}

That will ensure nothing happens if you are not on the route you want to watch.


Solution

  • I ran into the same problem. Instead of watching the current route, use vue-router onBeforeRouteUpdate, which only gets called if the route changed and the same component is reused.

    From https://next.router.vuejs.org/guide/advanced/composition-api.html#navigation-guards:

    import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
    import { ref } from 'vue'
    
    export default {
      setup() {
        // same as beforeRouteLeave option with no access to `this`
        onBeforeRouteLeave((to, from) => {
          const answer = window.confirm(
            'Do you really want to leave? you have unsaved changes!'
          )
          // cancel the navigation and stay on the same page
          if (!answer) return false
        })
    
        const userData = ref()
    
        // same as beforeRouteUpdate option with no access to `this`
        onBeforeRouteUpdate(async (to, from) => {
          // only fetch the user if the id changed as maybe only the query or the hash changed
          if (to.params.id !== from.params.id) {
            userData.value = await fetchUser(to.params.id)
          }
        })
      },
    }