Categories
Vue

Adding Actions to a Vuex Store

Spread the love

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

With Vuex, we can store our Vue app’s state in a central location.

In this article, we’ll look at how to add actions to our Vuex store to change the store’s state.

What are Actions?

Actions are similar to mutations, but they commit mutations instead of mutating the state. Actions can also commit asynchronous operations unlike mutations.

For example, we can add a simple action as follows:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increase(state) {
      state.count++;
    }
  },
  actions: {
    increase(context) {
      context.commit("increase");
    }
  }
});

An action takes a context object, which we can use to call commit to commit a mutation.

Dispatching Actions

We can dispatch actions by calling store.dispatch('increase') .

It’s much more useful than commit mutations directly because we can run asynchronous operations with it.

For example, we can dispatch an action as follows in Vue app:

index.js :

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increase(state, payload) {
      state.count += payload.amount;
    }
  },
  actions: {
    increaseAsync({ commit }, payload) {
      setTimeout(() => {
        commit("increase", payload);
      }, 1000);
    }
  }
});

new Vue({
  el: "#app",
  store,
  methods: {
    ...Vuex.mapActions(["increaseAsync"])
  },
  computed: {
    ...Vuex.mapState(["count"])
  }
});

index.html :

<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <meta charset="UTF-8" />
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vuex"></script>
  </head>
  <body>
    <div id="app">
      <button @click="increaseAsync({amount: 10})">Increase</button>
      <p>{{count}}</p>
    </div>
    <script src="index.js"></script>
  </body>
</html>

In the code above, we have the action increaseAsync with the following code:

increaseAsync({ commit }, payload) {
  setTimeout(() => {
    commit("increase", payload);
  }, 1000);
}

In the action method, we commit the increase mutation with a payload object that we pass in when we dispatch the action.

In the Vue instance, we have:

methods: {
    ...Vuex.mapActions(["increaseAsync"])
},
computed: {
  ...Vuex.mapState(["count"])
}

which maps the actions in the store by calling mapActions , and added computed properties by mapping them from the store by using mapState so we get a count property which is computed from state.count as a computed property.

Then in the template, we call increaseAsync when we press the Increase button to update state.count after 1 second.

Finally, we should see the number updated since we mapped the state from the store.

Composing Actions

We can chain actions that return promises.

For example, we can create an action to update 2 counts as follows:

index.js :

const store = new Vuex.Store({
  state: {
    count1: 0,
    count2: 0
  },
  mutations: {
    increase(state, payload) {
      state.count1 += payload.amount;
    },
    decrease(state, payload) {
      state.count2 -= payload.amount;
    }
  },
  actions: {
    async increaseAsync({ commit }, payload) {
      return Promise.resolve(commit("increase", payload));
    },
    async decreaseAsync({ commit }, payload) {
      return Promise.resolve(commit("decrease", payload));
    },
    async updateCounts({ dispatch }, payload) {
      await dispatch("increaseAsync", payload);
      await dispatch("decreaseAsync", payload);
    }
  }
});

new Vue({
  el: "#app",
  store,
  methods: {
    ...Vuex.mapActions(["updateCounts"])
  },
  computed: {
    ...Vuex.mapState(["count1", "count2"])
  }
});

index.html :

<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <meta charset="UTF-8" />
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vuex"></script>
  </head>
  <body>
    <div id="app">
      <button @click="updateCounts({amount: 10})">Update Counts</button>
      <p>Count 1: {{count1}}</p>
      <p>Count 2: {{count2}}</p>
    </div>
    <script src="index.js"></script>
  </body>
</html>

In the code above, we have the updateCounts action switch calls dispatch with the increaseAsync action and payload . Then it calls dispatch with the decreaseAsync action and payload .

increaseAsync commits the increase mutation, and decreaseAsync commis the decrease mutation.

Since they all have Promise.resolve , they’re all async.

Then we include the updateCounts action from the store in our Vue instance with mapActions . And we also include the count1 and count2 states with mapState .

Then when we click the Update Counts button, we call updateCounts , and then count1 and count2 are updated as we click the button. count1 should increase by 10 each time and count2 should decrease by 10 each time we click it.

Conclusion

We can use actions to commit one or more mutations or dispatch other actions.

It’s handy for grouping store operations together and running asynchronous code since mutations are always synchronous.

We can use mapActions to include them in our components.

Actions are dispatched by calling dispatch , while mutations are committed with the commit method.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *