Akshay Suresh Thekkath
Akshay Thekkath

Follow

Akshay Thekkath

Follow
Vuex - Vue State Management - Part III - Mutations

Vuex - Vue State Management - Part III - Mutations

Updating the global state using mutations

Akshay Suresh Thekkath's photo
Akshay Suresh Thekkath
·Mar 8, 2022·

4 min read

Once we have our state and getters ready as explained in part II of this blog, we can start defining our mutations.

What is a mutation?

Mutations are functions that are used to change/ modify your Vuex store. You cannot directly call your mutations instead a mutation can be invoked using store.commit.

Defining our first mutation

In the previous part, we defined our state and the getters. Let's create a mutation handler called CLEAR_WALLET_BALANCE which sets the walletBalance in the state to 0.

// store/index.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    userId: 101,
    deliveryPincode: "400068",
    walletBalance: 100.54,
    products: [
      { id: 1, title: "Samsung S21", price: 100 },
      { id: 2, title: "Macbook Pro", price: 200 },
      { id: 3, title: "Macbook Air", price: 300 },
      { id: 4, title: "Apple iPad", price: 400 },
      { id: 5, title: "Apple Watch", price: 500 },
    ],
    wishlist: [
      { id: 2, title: "Macbook Pro", isOutOfStock: false },
      { id: 4, title: "Apple iPad", isOutOfStock: true },
      { id: 5, title: "Apple Watch", isOutOfStock: true },
    ],
    cart: [
      { id: 4, title: "Apple iPad", price: 400, quantity: 1 },
      { id: 5, title: "Apple Watch", price: 500, quantity: 2 },
    ],
  },

  getters: {
    getCartTotalQuantity: (state) => {
      const quantities = state.cart.map((item) => item.quantity);
      return quantities.reduce((x, y) => x + y, 0);
    },
    getCartTotal: (state) => {
      const itemPrices
        = state.cart.map((item) => item.price * item.quantity);
      return itemPrices.reduce((x, y) => x + y, 0);
    },
    getWishlistOutOfStockItems: (state) =>
      state.wishlist.filter((item) => item.isOutOfStock).length,
  },

  mutations: {
    CLEAR_WALLET_BALANCE (state) {
      state.walletBalance = 0;
    }
  },

  actions: {},
});

We can call or commit this mutation inside our component using this.$store.commit. Now, whenever the button is clicked, the clearWalletBalance() method is called and this method will commit the CLEAR_WALLET_BALANCE mutation.

// ClearWalletBalance.vue
<template>
  <div>
    <button @click="clearWalletBalance">Clear Wallet Balance</button>
  </div>
</template>

<script>
export default {
  methods: {
    clearWalletBalance() {
      this.$store.commit('CLEAR_WALLET_BALANCE');
    }
  },
};
</script>

Committing using extra arguments

We can also pass a value to our mutation and this value can be set to any value in the state. For example, we can create a mutation to update the Pincode.

SET_DELIVERY_PINCODE (state, newPinCode) {
  state.deliveryPincode = newPinCode;
}

In the Component, the commit method will take the first parameter as the mutation name and the next parameter as the value.

// UpdatePinCode.vue
<template>
  <div>
    <input v-model="pinCode" type="text" />
    <button @click="updatePinCode">Update Pincode</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      pinCode: ""
    }
  },
  methods: {
    updatePinCode() {
      this.$store.commit('SET_DELIVERY_PINCODE', this.pinCode);
    }
  },
};
</script>

Similarly, we can also pass an Object to the mutation.

DECREMENT_WALLET_BALANCE (state, payload) {
  state.walletBalance = state.walletBalance - payload.amount;
}
this.$store.commit('DECREMENT_WALLET_BALANCE', {
  amount: this.amount
});

The mapMutations helper method

The mapMutations helper method can be used inside our component to list which mutations are required by the component.

<script>
import { mapMutations } from "vuex";

export default {
  methods: {
    ...mapMutations([
      'CLEAR_WALLET_BALANCE', 
      // maps `this.CLEAR_WALLET_BALANCE()` 
      // to `this.$store.commit('CLEAR_WALLET_BALANCE')`

      'SET_DELIVERY_PINCODE', 
      // maps `this.SET_DELIVERY_PINCODE(newPinCode)` 
      // to `this.$store.commit('SET_DELIVERY_PINCODE', newPinCode)`

      'DECREMENT_WALLET_BALANCE' 
      // maps `this.DECREMENT_WALLET_BALANCE(payload)` 
      // to `this.$store.commit('DECREMENT_WALLET_BALANCE', payload)`  
    ])
  },
};
</script>

Instead of a list as an argument to mapMutations, we can pass an Object with key as a method name and value as the mutation name. Now, the method name will be mapped to the mutation name.

<script>
import { mapMutations } from "vuex";

export default {
  methods: {
    ...mapMutations([
      clearWalletBalance: 'CLEAR_WALLET_BALANCE', 
      // maps `this.clearWalletBalance()` 
      // to `this.$store.commit('CLEAR_WALLET_BALANCE')`

      setDeliveryPincode: 'SET_DELIVERY_PINCODE', 
      // maps `this.setDeliveryPincode(newPinCode)` 
      // to `this.$store.commit('SET_DELIVERY_PINCODE', newPinCode)`

      decrementWalletBalance: 'DECREMENT_WALLET_BALANCE' 
      // maps `this.decrementWalletBalance(payload)` 
      // to `this.$store.commit('DECREMENT_WALLET_BALANCE', payload)`  
    ])
  },
};
</script>

Two-way computed property

We can also create two-way binding with our Vuex state using mutations. Here, we create a computed property where the getter will take the fetch the value from the state and the setter will call the mutation when any update occurs.

// UpdatePinCode.vue
<template>
  <div>
    <input v-model="pinCode" type="text" />
  </div>
</template>

<script>
export default {
  computed: {
    pinCode: {
      get () {
        return this.$store.state.deliveryPincode;
      },
      set (value) {
        this.$store.commit('SET_DELIVERY_PINCODE', value)
      }
    }
  }
};
</script>

This was all about mutations in Vuex. Next, we'll move to actions in Vuex!

Thanks for reading!

 
Share this