Can I call commit from one of mutations in Vuex store

I have a vuex store, like following:

import spreeApi from '../../gateways/spree-api'
// initial state
const state = {
  products: [],
  categories: []
}

// mutations
const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   commit('SET_CATEGORIES')
 },
 SET_CATEGORIES: (state) => {
   state.categories = state.products.map(function(product) { return product.category})
 }

}

const actions = {
 FETCH_PRODUCTS: (state, filters) => {
   return spreeApi.get('products').then(response => state.commit('SET_PRODUCTS', response))
 }
}

export default {
  state,
  mutations,
  actions
}

I want to call mutation: SET_CATEGORIES from mutation: SET_PRODUCTS, But this gives me error:

projectFilter.js:22 Uncaught (in promise) ReferenceError: commit is not defined(…)

What should be correct way to do this. I tried store.commit and this.commit, but these also gave similar errors.


If you absolutely must commit two mutations, why not do it from an action? Actions don't have to perform async operations. You can destructure the commit method in your action the same way you do with state like so:

commitTwoThings: ({commit}, payload) => {
  commit('MUTATION_1', payload.thing)
  commit('MUTATION_2', payload.otherThing)
}

For the record. To call other mutations from a mutation method do it like this:

const mutations = {
    mutationOne(state, payload){
        this.commit("mutationTwo", payload)
    },
    mutationTwo(state, payload){
        console.log("called from another mutation", payload)
    }
}

When you are already doing a mutation, there is no way to commit another mutation. A mutation is a synchronous call which changes the state. Within one mutation, you will not be able to commit another mutation.

Here is the API reference for Vuex: https://vuex.vuejs.org/en/api.html

As you can see, a mutation handler receives only state and payload, nothing more. Therefore you are getting commit as undefined.

In your case above, you can set the PRODUCT and CATEGORIES as part of the same mutation handler as a single commit. You can try if the following code works:

// mutations
const mutations = {
    SET_PRODUCTS_AND_CATEGORIES: (state, response) => {
        state.products = response.data.products
        state.categories = state.products.map(function(product) { return product.category})
    },
    // ...
}

EDIT: Please refer to the answer below, provided by Daniel S. Deboer. The correct method is to commit two mutations from a single action, as described in his answer.


To share code between mutations, you must create a new function that performs the work, which you can then reuse. Fortunately, mutations are just plain old functions, and we can pass the state parameter around however we like, so this is quite easy to do.

For example:

const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   setCategories(state)
 },
 SET_CATEGORIES: (state) => {
   setCategories(state)
 }
}

function setCategories(state) {
  state.categories = state.products.map(product => product.category)
}

And if I have some common code that affects state between multiple mutations, I have to duplicate the same code on all my mutations? Or there's a better way to do that?