Search code examples
vue.jsvuex

How to correctly order calls using Vue/Vuex


I have some skeleton code below which demonstrates an issue I'm having with forcing the correct order of calls. The order that I want is:

  • A user triggers Foo.vue's someLocalMethod().
    • A flag is set so that Bar will become aware that a change has been made.
  • The flag will trigger Bar.vues's watcher to go ahead and do some work.
    • When the work is complete it will unset the flag
  • Foo.vue's someLocalMethod() will become aware that the flag was unset and continue on with its work

I should mention that both Bar and Foo are doing work that changes the store, so the files are related (instead of solely using the store as a flag between both files).

I'm assuming there's a much better/cleaner way to approach what I'm doing, but I can't quite put my finger on it. Code is shown below.

Bar.vue

computed: {
  ...mapGetters('store', ['getSomeFlag']),
},
methods: {
  ...mapMutations('store', ['setSomeFlag']),
},
watch: {
  getSomeFlag(newValue) {
    if (newValue === true) {
      console.log('Flag is set. Going to do work');
      doWorkRelevantToBarPage();
      setSomeFlag(false);
    }
  }
}

Foo.vue

methods: {
  ...mapActions('store', ['someStoreCall']),
  ...mapMutations('store', ['setSomeFlag']),

  someLocalMethod(someData) {
    this.setSomeFlag(true);
    // somehow wait until Bar.vue's watcher becomes aware of it and completes it's own work
    this.someStoreCall(someData);      
  }
},

store.js

state: {
  someFlag: false,
  data: { 
    // Can be tons of nested structures, properties, etc
  },
},
getters: {
  getSomeFlag: (state) => state.someFlag,
},
mutations: {
  setSomeFlag(state, value) {
    state.someFlag = value;
  },
},
actions: {
  async someStoreCall({dispatch, commit, state}, data) {
    console.log('In someStoreCall');
  }
},

Solution

  • I would suggest using an event bus for this use case:

    1. in your Foo component's LocalMethod you emit an event (e.g. called "FooChanged") on the bus
    2. the event handler in Bar component (subscribed to "FooChanged" events) will do its work and then emit another event (e.g. called "BarFinished") on the bus
    3. the event handler in your Foo component (subscribed to "BarFinished" event) will continue with the next step in your flow

    Something like this:

    eventBus.js

    import Vue from "vue";
    export default new Vue();
    

    Foo.vue

    import events from "eventBus";
    
    export default
    {
      created()
      {
        events.$on("BarFinished", this.onBarFinished);
      },
      beforeDestroy()
      {
        events.$off("BarFinished", this.onBarFinished);
      },
      methods:
      {
        LocalMethod()
        {
          ....
          events.$emit("FooChanged", param1, param2);
        },
        onBarFinished(param1)
        {
          ...
        }
      }
    }
    

    Bar.vue

    import events from "eventBus";
    
    export default
    {
      created()
      {
        events.$on("FooChanged", this.onFooChanged);
      },
      beforeDestroy()
      {
        events.$off("FooChanged", this.onFooChanged);
      },
      methods:
      {
        onFooChanged(param1, param2)
        {
          ...
          events.$emit("BarFinished", param1);
        }
      }
    }