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 Vuex to our app and add a simple store.
What is Vuex?
Vuex is for storing states that are needed by multiple components in one central location.
It lets us get and set shared state and propagate any changes made to the shared state automatically to all components.
In the workflow diagram above, we can see that Vuex mutations are committed by our code when we get something from the back end API.
The mutation will update the state with the back end API data and the state will be updated in our Vue components.
We can also dispatch mutations from Vue components to change the Vuex store state the change will be propagated to all components that have access to the store.
Getting Started
We can include Vuex with a script
tag in our HTML code:
<script src="https://unpkg.com/vuex"></script>
Then we can create a simple store by using the Vuex.Store
as follows:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increase(state) {
state.count++;
}
}
});
The code above is a store which stores the state count
.
Then we can commit the increase
mutation by running:
store.commit("increase");
Then we can get the state after the mutation is done by running:
console.log(store.state.count);
store.state.count
is the count
state from the Vuex store. Then we should see 1 logged.
Getting Vuex State into Vue Components
We can get the state into our store by adding a computed property.
Therefore, to add the state into our store, we can write the following:
index.js
:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increase(state) {
state.count++;
}
}
});
new Vue({
el: "#app",
computed: {
count() {
return store.state.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">
<p>{{count}}</p>
</div>
<script src="index.js"></script>
</body>
</html>
Then we should see 0 displayed since count
is 0 initially in the store
.
Then whenever store.state.count
updates, the computed property will be updated and the view will update with the new value.
A more convenient way to inject the store into all child components is to add the store
in the root component as follows:
index.js
:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increase(state) {
state.count++;
}
}
});
new Vue({
el: "#app",
store
});
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">
<p>{{$store.state.count}}</p>
</div>
<script src="index.js"></script>
</body>
</html>
Then it’ll be available to all child components and we don’t have to worry about add computed properties for every value.
It works with child component without much changes:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increase(state) {
state.count++;
}
}
});
Vue.component("counter", {
template: `<div>{{ count }}</div>`,
computed: {
count() {
return this.$store.state.count;
}
}
});
new Vue({
el: "#app",
store
});
this.$store
is available to the counter
component just by including it in the root Vue component.
The mapState Helper
To avoid adding a new computed property for every state that’s in the store, we can use the mapState
helper to add it. For example, we can write the following code to do that:
index.js
:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increase(state) {
state.count++;
}
}
});
Vue.component("counter", {
data() {
return {
localCount: 1
};
},
template: `
<div>
<div>{{ count }}</div>
<div>{{ countAlias }}</div>
<div>{{ countPlusLocal }}</div>
</div>
`,
computed: Vuex.mapState({
count: state => state.count,
countAlias: "count",
countPlusLocal(state) {
return state.count + this.localCount;
}
})
});
new Vue({
el: "#app",
store
});
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">
<counter></counter>
</div>
<script src="index.js"></script>
</body>
</html>
Then we see:
001
Because we called mapState
as follows:
computed: Vuex.mapState({
count: state => state.count,
countAlias: "count",
countPlusLocal(state) {
return state.count + this.localCount;
}
})
We have:
count: state => state.count,
which gets the count
state from the store and returns it. The count
is 0 so we get 0.
Then we have:
countAlias: "count"
which is a shorthand for:
count: state => state.count
And finally, we have:
countPlusLocal(state) {
return state.count + this.localCount;
}
which adds state.count
from the store to this.localCount
, which we set to 1.
Object Spread Operator
We can combine local computed properties with mapState
by applying the spread operator to mapState
as follows:
index.js
:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increase(state) {
state.count++;
}
}
});
Vue.component("counter", {
data() {
return {
localCount: 1
};
},
template: `
<div>
<div>{{ count }}</div>
<div>{{ foo }}</div>
</div>
`,
computed: {
foo() {
return 2;
},
...Vuex.mapState({
count: "count"
})
}
});
new Vue({
el: "#app",
store
});
Then we get:
02
displayed since foo
always returns 2.
Components Can Still Have Local State
Components can still have their own local state, so we don’t have to put everything in the Vuex store.
Conclusion
We can add a Vuex store to our app to store the states of our app that are shared by multiple components.
To make getting state easy, we can include the store in the root Vue component.
Then we call the mapState
helper to get the states we want in any component.
We can also combine it with the local states with the spread operator in the computed
object.