'How can I tell if a Vue component is unmounted to prevent async function accessing unmounted component's data?

Please see this minimum example

import Vue from 'vue';

Vue.extend({
  data() {
    return {
      name: 'James',
    };
  },
  methods: {
    greet() {
      setTimeout(() => {
        const componentIsUnmounted = ???; // How do I tell if component is unmounted?

        if (!componentIsUnmounted) {
          console.log(this.name);
        }
      }, 300);
    },
  },
});

As you can see, I have a component with an async function, it will be triggered after 300ms when you called it, by that time, the Vue component might be unmounted.

I think I can do this by storing a flag in the global via Lodash's uniqueID() function to create a unique ID at mounted() and beforeDestroyed().

Is there another easyier way to do that?



Solution 1:[1]

I think that it would be best if you can control the timeout (using clearTimeout(), for example, as suggested on the comments). In your case, as you're using a debounce, depending on what lib you're using, you might not have that control.

In this case, one suggestion is to mix Node.contains with vm.$el. As follows:

export default {
  methods: {
    greet() {
      const isUnmounted = !document.body.contains(this.$el)
    }
  }
}

Other alternative is to use destroyed lifecycle to control this state internally.


I've provided a guiding example that might help you: https://codesandbox.io/s/smoosh-frost-fdisj.


Hope it helps!

Solution 2:[2]

Here is what I just used and it worked great.

When you create the timeout, store the ID on this. Then on beforeDestroy, call clearTimeout with the ID. Even if you manually cancel the timeout beforehand, clearTimeout will not fail.

<script>
  export default {
    created() {
      this.timeoutId = setTimeout(() => {}, 1000)
    },

    beforeDestroy() {
      clearTimeout(this.timeoutId)
    }
  }
</script>

No need to set it in the data property, as it isn't reactive.

Solution 3:[3]

You can use the beforeDestroy event like below :

  mounted() {
    this.$once("hook:beforeDestroy", () => {
      //...
    })
  }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 William Martins
Solution 2 Jtcruthers
Solution 3 Samidjo