foxbenjaminfox/vue-async-computed

Provide option to exclude or explicitly list watched properties

davidje13 opened this issue · 0 comments

I have just encountered a situation where I managed to get an infinite loop due to updating vuex state as part of an asynchronously computed property. In fairness I realise this is against the Vue principles but it is the only way I know of to display a global loading indicator.

The basic setup:

asyncComputed: {
  async thing() {
    console.log('beginning');
    const loadId = {};
    this.$store.dispatch('beginLoad', loadId);
    try {
      await new Promise((resolve) => setTimeout(resolve, 1000);
    } finally {
      this.$store.dispatch('endLoad', loadId);
    }
    console.log('ending');
    return 1;
  },
}

Store setup:

new Vuex.Store({
  state: { loader: [] },
  mutations: {
    BEGIN_LOAD: (state, id) => { state.loader.push(id); },
    END_LOAD: (state, id) => { state.loader.splice(pos, state.loader.indexOf(id)); },
  },
  actions: {
    beginLoad({ commit }, id) { commit('BEGIN_LOAD', id); },
    endLoad({ commit }, id) { commit('END_LOAD', id); },
  },
}

This results in an infinite loop, where the next call is made before the first has entirely finished;

beginning
beginning
ending
beginning
ending
beginning
ending
[...]

It appears to be caused because the change to the loading state at the end triggers a new update.

I would like to be able to explicitly remove the $store as a watched variable, or explicitly list the properties I actually care about. I can achieve this with shouldUpdate by storing previous variables and checking if anything I care about changed, but that makes shouldUpdate change state which is also not good (and makes any explicit update() calls stop working).

If this is not a use-case you want to support, I suggest at least making sure there are not two invocations in-flight at the same time; if inputs change, it should wait until the current request has finished before starting a new one