58

Can someone explain when you would use a dispatch versus a commit?

I understand a commit triggers mutation, and a dispatch triggers an action.

However, isn't a dispatch also a type of action?

cching
  • 789
  • 1
  • 8
  • 17

1 Answers1

145

As you rightly said, $dispatch triggers an action, and commit triggers a mutation. Here is how you can use these concepts:

You always use $dispatch from your methods in routes / components. $dispatch sends a message to your vuex store to do some action. The action may be done anytime after the current tick, so that your frontend performance is not affected.

You never commit from any of your components / routes. It is done only from within an action, and only when you have some data to commit. Reason: commit is synchronous and may freeze your frontend till it is done.

Let's consider this case: If you have to fetch some json data from server. In this case, you need to do this asynchronously so that your user interface is not unresponsive / frozen for a while. So, you simply $dispatch an action and expect it to be done later. Your action takes up this task, loads data from server and updates your state later.

If you need to know when an action is finished, so that you can display an ajax spinner till then, you may return a Promise as explained below (example: load current user):

Here is how you define the "loadCurrentUser" action:

actions: {
    loadCurrentUser(context) {
        // Return a promise so that calling method may show an AJAX spinner gif till this is done
        return new Promise((resolve, reject) => {
            // Load data from server
            // Note: you cannot commit here, the data is not available yet
            this.$http.get("/api/current-user").then(response => {
                // The data is available now. Finally we can commit something
                context.commit("saveCurrentUser", response.body)  // ref: vue-resource docs
                // Now resolve the promise
                resolve()
            }, response => {
                // error in loading data
                reject()
            })
        })
    },
    // More actions
}

In your mutations handler, you do all the commits originating from actions. Here is how you define the "saveCurrentUser" commit:

mutations: {
    saveCurrentUser(state, data) {
        Vue.set(state, "currentUser", data)
    },
    // More commit-handlers (mutations)
}

In your component, when it is created or mounted, you just call the action as shown below:

mounted: function() {
    // This component just got created. Lets fetch some data here using an action
    // TODO: show ajax spinner before dispatching this action
    this.$store.dispatch("loadCurrentUser").then(response => {
        console.log("Got some data, now lets show something in this component")
        // TODO: stop the ajax spinner, loading is done at this point.
    }, error => {
        console.error("Got nothing from server. Prompt user to check internet connection and try again")
    })
}

Returning a Promise as shown above is entirely optional and also a design decision not preferred by everyone. For a detailed discussion on whether to return a Promise or not, you may read the comments under this answer: https://stackoverflow.com/a/40167499/654825

Community
  • 1
  • 1
Mani
  • 23,635
  • 6
  • 67
  • 54
  • 7
    Just for the record - you can commit from a component - it just should be synchronous (meaning no ajax). The issue is not about locking the front-end - it's about confusing flows (I commited a change, but it's not there yet - oh the ajax didn't finish..) If you are commiting a state value that doesn't need to be persisted anywhere, then it's fine to do from the component. This is how I understand from my research - corrections welcome. – Yehosef Mar 05 '18 at 10:33
  • just checked https://vuex.vuejs.org/en/mutations.html - it talks about committing mutations in components. – Yehosef Mar 05 '18 at 10:37
  • 1
    @Yehosef Agreed! That was my understanding more than a year ago, when the docs at that time recommended to always commit from actions and never from components directly. I agree with you that we can commit mutations directly from components for simple values, as long as we are sure that it won't cause any race conditions leading to unpredictable behavior. – Mani Mar 06 '18 at 11:16
  • 1
    @Mani Do you commit or dispatch when you want to make cross-module call? – Bertie92 Mar 28 '19 at 11:07
  • @Bertie92 I haven't yet had a requirement to make cross-module calls yet. Do you mean this - https://vuex.vuejs.org/guide/modules.html? In any case, I believe the right approach should be to always dispatch an action if we are doing it outside that vuex module. Only the action (belonging to that vuex module) should commit and mutate the state within that module. – Mani Mar 30 '19 at 06:39
  • @Mani Yes, I meant that. I think you are right, that's how I'd do it. But with small apps I just commit if there's nothing async or complex logic behind it. I guess when it comes to big apps you have to use dispatch all time to make sure it works flawlessly. – Bertie92 Apr 01 '19 at 10:48
  • 1
    @Bertie92 I agree, we should be able to directly commit for small apps. At a conceptual level, I would like to think of every store module as a [module pattern](https://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript), with action handlers as public interfaces and commits as private methods. With that kind of conceptual model in mind, a direct commit is equivalent to calling a private method of a module from outside, which seems risky, but should be ok for small apps. – Mani Apr 02 '19 at 07:52