What are Vuex Actions?
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>`
});