The way of creating websites has changed a lot over the last decade. Currently, the trend is to use frameworks like React, Angular, or Vue. While each has its peculiarities, they all meet on the concept of component. A component is simply a self-contained part of your applications, typically made with HTML, CSS, and JS. An example of a component could be a pop-up message or a date picker. Since we use components to separate functionalities, sharing information among them can be hard. Fortunately, Vuex makes up for that. In this Vuex Store Tutorial, we will see how to use Vuex to share information among components.
Tip: this article requires at least basic knowledge of JavaScript and Vue.
The problem
Before we dive into the solution with our Vuex store tutorial, we better understand the problem. Components rely on information to do things, for example, a drop-down may be opened or closed based on the value of a variable. So far so good, but the real twist happens when many components work on the same variable. For example, the “Login” component updates the user data, and we want to see that data in the “Toolbar” component as well.
A quick solution is a top-down approach using v-bind
. With this approach, the parent component passes information to the child component when declaring it. While this is a bidirectional binding for simple types, we might have problems with objects. Furthermore, we need to use it on all components between parent and child to maintain the chain. That’s not a real solution. Another option would be using busses, but that’s where things get really messy.
Vuex allows a better flow, based on actions, mutations, and state, as we will see. Here is a visualization of how Vuex approaches this problem.
Vuex Store Tutorial
Getting vuex
Vuex is a npm package like anything else in your Vue application. Thus, we can install it with the following command.
npm install vuex --save
Note the --save
option, it will add vuex to your dependencies in your package.json
. That’s important if you want to make your application portable and build on other devices. Besides installing vuex, this command might also set-up some sample files with a basic store, depending on the version.
Creating the Vuex Store
The first part of this Vuex Store tutorial is creating a store. A store is simply a container where you want to store your application data. Furthermore, this store is accessible natively in all your components, regardless of their location. In the end, the store is just a container of variables and functions to modify them. Let’s first create some variables.
Inside your src
folder, create a new sub-folder named store
. Here, we will put all the files that relate to our store. The first one will be index.js
. Here, we can add the following code.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
/* eslint-disable no-param-reassign */
export default new Vuex.Store({
state: {
userInformation: null,
loggingIn: false
}
})
After the imports, we tell our Vue engine to use Vuex. Then, we export a store that has two variables in its state: userInformation
and loggingIn
, with default values. We will use the first to store information about a logged user, and the second to toggle loadings in case the user is attempting to log in.
The next step in our vuex store tutorial is to include our store in the Vue application. This is done inside main.js
. We add the import of the router (we don’t need to include the file name index.js
because it is the index, just the folder will do). Then, we simply pass the store to our Vue app.
import store form './store'
new Vue({
store,
// existing Vue settings here...
}).mount('#app') // or whatever mount point you have
Accessing the Vuex Store state from a component
Vue will “install” our Vuex Store inside its engine, and make it available to any component in our app. Thus, in our methods and computed properties of each component, we can access the state of the store in read-only. To access a state, simply use the following code.
let myVariable = this.$store.state.userInformation
Mutations
At this point in our vuex store tutorial, the store is still useless. This is because the variables inside the state are read-only from outside the store. To modify them, we have two options: mutations and actions. Mutations are the simplest ones, they allow sychronous modification. We can add them with the mutations
object inside our store. Each mutation is a function that accepts a state as a parameter, and optionally a payload of information.
export default new Vuex.Store({
state: {
userInformation: null,
loggingIn: false
},
mutations: {
login (state, payload) {
state.userInformation = payload
},
attemptLogin (state) {
state.loggingIn = true
},
finishAttemptLogin (state) {
state.loggingIn = false
}
}
})
To trigger a mutation from a component, you need to commit
it. This can be done easily with the following code.
this.$store.commit('login', { name: 'John Doe' })
this.$store.commit('attemptLogin')
Actions
With actions, our vuex store tutorial becomes really interesting. They allow asynchronous modifications to the state. Yet, their structure is very similar to the mutations we have already seen. We add them in the actions
object.
export default new Vuex.Store({
state: {
userInformation: null,
loggingIn: false
},
mutations: {
login (state, payload) {
state.userInformation = payload
},
attemptLogin (state) {
state.loggingIn = true
},
finishAttemptLogin (state) {
state.loggingIn = false
},
},
actions: {
authenticate ({ commit }, { username, password }) {
commit('attemptLogin')
const data = new FormData()
data.append('name', username)
data.append('password', password)
axios({
method: 'post',
url: '/my-authentication-url',
data: data,
config: { headers: { 'Content-Type': 'multipart/form-data' } }
})
.then(response => {
if (response.status === 200) {
commit('login', response.data)
}
})
.then(function () {
commit('finishAttemptLogin')
})
}
}
})
Each action wants a commit
as a first parameter. This is simply a callback to the commit function of this store. Then, it can optionally accept other parameters in a object (here we want to know username and password). Our authenticate function commits attemptLogin
to indicate the user is logging in, then performs an asynchronous request to the server. If the request succeeds, it triggers the login mutation. In the end, it always triggers the finishAttemptLogin
.
To call an action from a component you we use the dispatch
function.
this.$store.dispatch('authenticate', { 'my-username', 'my-password' })
Getters
As we have already seen in our vuex store tutorial, state stores information. However, we might want to use some information that derives directly from the state. In other words, we might want to have computed properties in our vuex store as well. We can do that, and they are called getters. A getter is a function inside the getters object that wants the state as first parameter. Optionally, it can get the getters as the second parameter: this allows your function to rely on other getters as well.
export default new Vuex.Store({
// State, mutations and actions (omitting for brevity) ...
getters: {
loggedIn: function (state) {
return state.userdata !== null
},
userName: function (state, getters) {
if (getters.loggedIn) {
return state.userdata.name
}
return null
}
}
})
Now, we can access the getters anywhere in our component by using the getters
property of the store.
let var1 = this.$store.getters.loggedIn
let var2 = this.$store.getters.userName
mapGetters & mapActions
Often times, we want to have a computed property in our component that returns the value of a getter in our state. On top of that, we might want to do the same with methods: a method that dispatches an action of our vuex store. Creating the method and calling the store action or getter is simply code redundancy, which is very bad. Luckily, Vuex comes with two methods that help us in this case.
With mapGetters
, you can select a set of getters of your vuex store and install them as computed properties in your component. They will have the same name of the vuex getter. Use it as below.
import { <span class="hiddenSpellError" pre="import " data-mce-bogus="1">mapGetters</span> } from 'vuex'
export default {
name: 'MyComponent',
computed: {
...mapGetters([
'loggedIn',
'userName'
])
}
}
The same is true with mapActions
, but it is valid for methods instead of computed properties.
import { <span class="hiddenSpellError" pre="import " data-mce-bogus="1">mapActions</span> } from 'vuex'
export default {
name: 'MyComponent',
methods: {
...mapActions([
'authenticate'
])
}
}
Vuex store outside this.$store
So far in this vuex store tutorial, we have seen the magic of using this.$store
property. However, this represents the Vue VM, and it means we can access it only from within components. What if we need to access the vuex store from outside a component? This is possible by importing the store. It won’t be a new instance, and use the same data in use inside your Vue VM.
import store from '../store'
let var = store.getters.loggedIn
Wrapping it up
In this vuex store tutorial, we have seen how to implement state management in vue. This is possible thanks to several great components like state, mutations, actions and getters. If you need more information, you can take a look at the official documentation. Instead, if you are in a hurry here we have a quick recap:
- The state contains information, read-only from outside the store. Access the state object from any component with this.$store.state
- Mutations allow you to change the value of the state synchronously, call them with
this.$store.commit('mutationName', parameter)
- Actions are similar to mutations, but they are asynchronous. Call them with
this.$store.dispatch('actionName', parameter)
- You can get computed properties of a store from the getters, with
this.$store.getters.getterName
- You can map getters and actions of your vuex store in a component with
mapGetters
andmapActions
What do you think of the Vuex store? Do you think you will have a better and cleaner code by using it? Let me know in the comments!