Joseph
Joseph

Reputation: 4725

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?

Upvotes: 7

Views: 6483

Answers (3)

William Martins
William Martins

Reputation: 2067

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!

Upvotes: 1

Jtcruthers
Jtcruthers

Reputation: 956

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.

Upvotes: 3

Samidjo
Samidjo

Reputation: 2355

You can use the beforeDestroy event like below :

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

Upvotes: 0

Related Questions