Saurabh
Saurabh

Reputation: 73649

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.

Upvotes: 119

Views: 132169

Answers (16)

Synchro
Synchro

Reputation: 1269

In Vuex, mutations are synchronous functions that are responsible for modifying the state of the Vuex store. While mutations cannot directly call other mutations, you can achieve the desired behavior by utilizing actions.

Here's an example of how you can call one mutation from another mutation using actions:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 0,
  },
  mutations: {
    increment(state) {
      state.count++;
    },
    doubleIncrement(state) {
      state.count += 2;
    },
  },
  actions: {
    incrementAndDouble({ commit }) {
      commit('increment');
      commit('doubleIncrement');
    },
  },
});

export default store;


<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="incrementAndDouble">Increment and Double</button>
  </div>
</template>

<script>
import { mapState, mapActions } from 'vuex';

export default {
  computed: {
    ...mapState(['count']),
  },
  methods: {
    ...mapActions(['incrementAndDouble']),
  },
};
</script>

Upvotes: 0

Mani Jagadeesan
Mani Jagadeesan

Reputation: 24275

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.

Upvotes: 65

Sarwar Hasan
Sarwar Hasan

Reputation: 1641

Use this

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

Upvotes: 0

Mohamed EL YONOUSI
Mohamed EL YONOUSI

Reputation: 127

you can access to all vuex

this.app.store.commit("toast/show", {
                   mssg:this.app.i18n.t('order.ordersummary.notifymessage'),
                    type: "danger",
                });

access to $i18n in vuex

this.app.i18n.t('order.ordersummary.notifymessage')

Upvotes: 0

JeffNhan
JeffNhan

Reputation: 363

another solution that works for me:

this._mutations.mutationFunction[0]()

Upvotes: 1

Balaji
Balaji

Reputation: 10997

i think

calling mutation from another mutation is bad idea because of hard to debug state and components

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

but you can write simple function and function can reusable

function mysecondfn(state,payload){
{
// do your stuff here
}


const mutations = {
    mutationOne(state, payload){
mysecondfn(state,payload)
     },

}

Upvotes: 1

Amio.io
Amio.io

Reputation: 21585

I prefer to call mutations.SET_CATEGORIES(state) instead of: - calling 2 different commits from an artificial action - or doing commit() inside a mutation as it makes unit testing more difficult.

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

My opinion is that you don't need to see SET_CATEGORIES in the VueToolbox. The time travel should work anyways. Please, correct me if I'm wrong.

Upvotes: 3

Ali KOCA
Ali KOCA

Reputation: 21

First, assign the Vue button to a variable: In main.js:

  export const app = new Vue({  
  router,
  vuetify,
  store,....

Then import the "app" variable to the js file where you define the mutation: In modules.js:

import { app } from "../../main";

You can now use it as "app.$store.commit":

mutations: {
[AUTH_SET_TOKEN]: () => {
app.$store.commit(USER_SUCCESS, params );
},...

Upvotes: 2

Kubwimana Adrien
Kubwimana Adrien

Reputation: 2531

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)
    }
}

Upvotes: 143

Andrei
Andrei

Reputation: 19

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

// mutations
const mutations = {
 SET_PRODUCTS: (state, {response,commit}) => { // here you destructure the object passed to the mutation to get the response and also the commit function
   state.products = response.data.products
   commit('SET_CATEGORIES') // now the commit function is available
 },
 SET_CATEGORIES: (state) => {
   state.categories = state.products.map(function(product) { return product.category})
 }

}

const actions = {
 FETCH_PRODUCTS: ({commit}, filters) => { // here you destructure the state to get the commit function
   return spreeApi.get('products').then(response => commit('SET_PRODUCTS', {response,commit})) // here you pass the commit function through an object to 'SET_PRODUCTS' mutation
 }
}

export default {
  state,
  mutations,
  actions
}

This should fix it. You can inject the commit into your mutation from the action so you can commit from your mutation. Hope this helps

Upvotes: 0

Guillaume Meral
Guillaume Meral

Reputation: 462

Edit : I stumbled upon a very similar problem and the solution for me was to use a vuex getter : https://vuex.vuejs.org/en/getters.html
Your categories is actually a "computed" version of your products. Having categories as a getter allows you to keep them in sync with products and avoids duplicating the data in your store.

For the sake of answering the question in the title i leave my original answer.
An alternative to Daniel Buckmaster solution :

const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   this.SET_CATEGORIES(state)
 },
 SET_CATEGORIES: (state) => {
   state.categories = state.products.map(product => product.category)
 }
}

As you can see you could directly call the mutation itself. (as Daniel said, they are just plain functions after all)
I believe that this is a more appropriate answer to the original question : it is an actual way of composing mutations without code duplication or extra functions

Upvotes: 3

Wanny Miarelli
Wanny Miarelli

Reputation: 744

Reading the Vuex documentation on Actions, it's quite clear what they are made for.

  • commit mutations instead of mutating the state
  • can contain arbitrary asynchronous operations

Actions can (not must) contain asynchronous code. In fact, the following example is correct

increment (context) {
   context.commit('increment')
}

I do not see any issue in using actions for performing multiple mutations.

Upvotes: 9

Daniel S. Deboer
Daniel S. Deboer

Reputation: 1566

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)
}

Upvotes: 155

jiv-e
jiv-e

Reputation: 513

In your case you should consider having only one mutation, namely SET_PRODUCTS.

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

You should never have any need to call SET_CATEGORIES separately. Think about it! Categories can only mutate if products are changed. And products can change only through SET_PRODUCTS.

Upvotes: 4

Daniel Buckmaster
Daniel Buckmaster

Reputation: 7186

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)
}

Upvotes: 35

Nacho
Nacho

Reputation: 199

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?

Upvotes: 18

Related Questions