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.