What are Vuex Actions?

Nov 13, 2020

In Vuex, actions are functions that call mutations. Actions exist because mutations must be synchronous, whereas actions can be asynchronous.

You can define actions by passing a POJO as the actions property to the Vuex store constructor as shown below. To "call" an action, you should use the Store#dispatch() function.

const Vuex = require('vuex');

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment: state => ++state.count
  },
  actions: {
    incrementDelay: async function(context) {
      // Wait 50ms
      await new Promise(resolve => setTimeout(resolve, 50));

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

// To "call" an action, you should use call `dispatch()` function with
// the action's name.
await store.dispatch('incrementDelay');
store.state.count; // 1

What's the Point of Actions?

The obvious question to ask about actions is "why actions?" Vuex stores have a commit() function that lets any function commit mutations, so you could just as easily do this:

const Vuex = require('vuex');

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment: state => ++state.count
  }
});

async function incrementDelay() {
  // Wait 50ms
  await new Promise(resolve => setTimeout(resolve, 50));

  store.commit('increment');
}

// Instead of dispatching an action, you could just call an
// async function.
await incrementDelay();
store.state.count; // 1

In isolation, the async function approach above is better because it doesn't depend on any particular framework. You can just call a function and that's it. Plus you can just use incrementDelay() as a method on your Vue instance and get error handling for free.

But there's one very cool benefit of using actions: the subscribeAction API. Vue lets you register a callback that Vue will call every time an action is dispatched.

const Vuex = require('vuex');

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment: state => ++state.count
  },
  actions: {
    incrementDelay: async function(context) {
      // Wait 50ms
      await new Promise(resolve => setTimeout(resolve, 50));

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

store.subscribeAction(function callback(action, state) {
  // Prints "{ type: 'incrementDelay', payload: undefined }"
  console.log(action);
});

// To "call" an action, you should use call `dispatch()` function with
// the action's name.
await store.dispatch('incrementDelay');
store.state.count; // 1

The subscribeAction() API is the basis for many Vuex plugins, so using actions can let you better leverage the Vue community's plugins.

mapActions()

Actions are great, but how do you use them with Vue components? Vuex has a neat mapActions() function that converts actions to Vue instance methods as shown below.

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment: state => ++state.count
  },
  getters: {
    count: state => state.count
  },
  actions: {
    incrementDelay: async function(context) {
      // Wait 50ms
      await new Promise(resolve => setTimeout(resolve, 50));

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

const app = new Vue({
  store,
  methods: Vuex.mapActions(['incrementDelay']),
  computed: Vuex.mapGetters(['count']),
  mounted: async function() {
    await this.incrementDelay(); // Dispatches "incrementDelay"
  },
  // Displays 0, then 1 after 50ms
  template: `<h1>{{count}}</h1>`
});

Vue School has some of our favorite Vue video courses. Their Vue.js Master Class walks you through building a real world application, and does a great job of teaching you how to integrate Vue with Firebase. Check it out!


Did you find this tutorial useful? Say thanks by starring our repo on GitHub!

More Vue Tutorials