Getting started with Vuex: A beginners guide
Introduction:
This is a brief tutorial that is intended to bring an absolute beginner up to speed with Vuex. Previous knowledge of Vue.js is required to follow along.
we hope to cover the following key factors by the end of the tutorial:
- What is Vue.js store/Vuex
- How Vuex works
- The Vuex store
- Working with state
- Getters
- Mutations
- Actions
What is Vuex/Vue.js Store:
Vuex is a central store for Vue.js applications. Data stored in the Vuex store can be accessed from any component in the application. Sharing of data in this manner solves the problem of prop drilling ( passing data through components that do not need it in order to get them to where they are needed).
Vue.js makes the building of single-page applications easy and straight forward. Single-page applications are applications that work inside the browser and do not need a page to reload during usage. They dynamically re-write the current page.
One of the major challenges of building a SPA with Vue.js is passing data (props) from one Vue.js component to another.
Vuex is the official Vue.js library that solves storage issues and can be referred to as the central point of truth. Data stored in it can be accessed from any component within the application.
A store can be set up in Vue.js like so:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
//code goes here..
})
How Vuex store works:
We need to get a basic understanding of how the Vuex code is organized and how it works. Vuex is a flux-based state management framework. If you’re familiar with similar frameworks such as Redux, you shouldn’t find anything too surprising here.
In a typical Vue.js application, components need to access data in the store and in other cases need to update the data in the store. In the store, there is a state object which holds every stored data. This is what the state looks like
state:{
// put variables and collections here
}
To access data in the store, we use the:
getters:{
//code goes here
}
To call the mutations in Vue.js components, we use the:
actions:{
//code goes here
}
To update or change the data in the store, we use the:
mutations:{
//code goes here
}
Let us now take them in detail.
Vuex Store in a real-world application
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// put variables and collections here
},
getters: {
// put sychronous functions for changing state e.g. add, edit, delete
},
mutations: {
// put sychronous functions for changing state e.g. add, edit, delete
},
})
After the store has been defined, it needs to be utilized in the application by injecting the store into the app. This can be achieved like so:
// src/main.js
import store from './store'
new Vue({
store,
render: h => h(App)
}).$mount('#app')
At the point, we can access the store in any component using
this.$store
The State:
The state is a property of the store. It is an object that holds every data in the store. Data in the state can be in the form of objects, arrays, strings and any other data type supported by Vue.js. Below is a state containing an example of data.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
apple: ''
}
})
apple:''
can be accessed, changed or updated from any component within the app using getters and mutations.
Getters:
Getters provide a way of retrieving the values we want from the state object. A getter’s result is cached based and will only re-evaluate when some of its dependencies have changed. They are a lot similar to computed properties for stores. Getters expect the state to be passed in as an argument like so:
const store = new Vuex.Store({
state: {
apple: "This is some string data in the state"
},
getters: {
getApple: state => {
return state.apple
}
}
})
from the above, the getApple method in the getters can be used to access the value of apple in the state object like so:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
apple: ''
},
mutations: {
changeAppleValue(state, apple) {
state.apple = apple
}
}
})
The mutations object can be called easily in components using
this.$store.commit('changeAppleValue', value)
The commit( ) method:
Noticed we used the commit method to call the mutation and the respective state value. This method accepts the mutation name as the first parameter and the payload as the second parameter. The commit method is used to call the mutation.
Actions:
Actions are functions that don’t change the state themselves. Instead, they commit mutations after performing some logic (which is often asynchronous).
Actions are similar to mutations, the differences being that:
- Instead of mutating the state, actions commit mutations.
- Actions can contain arbitrary asynchronous operations.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Actions are called in the components with the method.
store.dispatch()
The action in our example above can be called like so:
store.dispatch('increment')
Building a Fun interactive app with Vuex:
- Create a new Vue project:
Let’s build a fun project to better understand Vuex. We will be building a simple app interactive app that explains how Vuex works to the players of the users.
Let’s get started by creating a new Vue project using Vue CLI (command-line tool for Vue.js). If you do not have Vue CLI installed, click here to install.
Vue create vuex-example
select the manual option while creating the project and install Vuex.
- Add Bootstrap:
We will style our Vue components using bootstrap. We will be referencing Bootstrap from the CDN ( Content Delivery Network). Navigate to index.html in your project’s public folder.
Add the Bootstrap CDN to the head.
<!-- bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
At the bottom of the body tag, add optional Bootstrap js CDN:
https://code.jquery.com/jquery-3.3.1.slim.min.js
https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js
https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js
- Create our Vuex store:
Let us create a new file called store.js. You can actually name it whatever you like. Create a new Vuex store in it like so:
// import vue and vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// create our store
export default new Vuex.Store({
state:{
message: "Hi, I am coming from the state that is why you can find me in every component with passing props"
}
})
Here we imported and used Vuex and Vue, then we created a new Vuex store with our state object. The state object contains the message which is our payload in this instance.
- Add the store to main.js:
Import and use the store.js file in our main.js like so:
import store from './store' //import store.js page
Then add the store to the vue instance
new Vue({
render: h => h(App),
store //store added here
}).$mount('#app')
Now, this.$store is accessible from any component in the app.
- Create Our Getters:
We need to display the data in the state on our page. We can get this done using getters like so:
// create our store
export default new Vuex.Store({
state:{
message: "Hello, I am coming from your Vuex state that is why you can see me here and in every other component without passing props"
},
//added a getMessage getter
getters: {
getMessage(state){
return state.message
}
},
})
- Style our landing page:
Head on to the HelloWorld.vue component removes all irrelevant code, add the following block of code to it.
<template>
<div class="hello">
<div class="container">
<div class="">
<div class="col-md-12">
<div class="form-group">
<small class="btn-outline-danger">Getters provide a way of retrieving the values we want from the state object.
A getter's result is cached based and will only re-evaluate when some of its dependencies have changed.
</small>
</div>
<div class="card">
<div class="card-body">
<h1>this.$store.getters was used</h1>
<P>{{stateData}}</P>
<router-link to="/update" class="btn btn-outline-secondary">
Next
</router-link>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return{
stateData:''
}
},
mounted(){
this.stateData = this.$store.getters.getMessage
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
Notice how we used our getMessage
getter method this.$store.getters.getMessage
to get the data in the state. This data is then displayed in the template section as state Data. If everything goes fine, your page should look like so:
- Adding Vue router:
Our app now needs to navigate from one page to another. Let’s install vue router using npm like so:
npm install vue-router
create a router.js file and modify it like so:
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
let router = new VueRouter({
// mode: 'history',
routes:[
{path:'/', name:'Landing', component: () =>import('./components/HelloWorld')},
{path:'/update', name:'Update', component: () =>import('./components/UpdateState')},
]
});
export default router;
Import the router.js file into the main.js.
import router from './router' //import router.js into main.js
add the router to the vue instance
new Vue({
render: h => h(App),
store,
router //router added
}).$mount('#app')
We created two routes. One for the Landing page and another for the UpdateState component. Create a component with the name UpdateState and add the below code to flesh it out.
<template>
<div class="hello">
<div class="container">
<div class="">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<h1>Page 2</h1>
<P>{{stateData}}</P>
<div class="form-group">
<input type="text" v-model="updateState" class="form-control">
</div>
<button @click="Update()" class="btn btn-outline-secondary">
Update state
</button>
<router-link to="/" @click="Update()" class="btn btn-outline-secondary">
Back
</router-link>
<hr>
<div class="form-group">
<small class="btn-outline-danger">
Here We used Actions to dispatch our mutaions.
Actions are similar to mutations,
the differences being that:
- Instead of mutating the state, actions commit mutations.
- Actions can contain arbitrary asynchronous operations.
</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return{
stateData:'',
updateState:''
}
},
mounted(){
this.stateData = this.$store.getters.getMessage
},
methods:{
Update(){
alert('hello');
}
}
}
</script>
Our UpdateState
component should look like :
Now let us update the state when the update state button is clicked. First, we need to validate the form like so:
methods:{
Update(){
if(this.updateState != ""){
}else{
alert('Emtyp form field')
}
}
}
- Mutations:
We can now proceed to update the state. In your store.js file create a new mutation like so:
// instantiate our store
export default new Vuex.Store({
state:{
message: "Hello, I am coming from your Vuex state that is why you can see me here and in every other component without passing props"
},
//instantiate our mutation
mutations:{
changeMessageValue(state, message){
state.message = message
}
}
})
This mutation accepts the state as its first argument and the message as the second. We can now use it to update our state with the this.$store.commit()
method like so:
Head over to your updateState
component and modify it like so:
methods:{
Update(){
if(this.updateState != ""){
//using the commit method to call the mutation
this.$store.commit('changeMessageValue', this.updateState)
//update the state with the curren
this.stateData = this.updateState
alert('state updated');
}else{
alert('Emtyp form field')
}
}
}
We used our commit method to implement the mutation above.
- Actions:
We really do not need to commit our mutations directly in large applications. It is better to use actions. Let’s look at how Actions work. create a new action in our store.js file like so:
// create our store
export default new Vuex.Store({
state:{
...
},
getters: {
...
},
mutations:{
changeMessageValue(state, message){
state.message = message
},
},
//noticed we added our action to commit our mutation
actions:{
changeMessageValue(context, message){
context.commit('changeMessageValue', message)
}
}
})
let us also modify our updateState
component to dispatch our action.
methods:{
// mutation
Update(){
if(this.updateState != ""){
this.$store.dispatch('changeMessageValue', this.updateState)
this.stateData = this.updateState
alert('state updated');
}else{
alert('Emtyp form field')
}
}
}
Actions are a lot more power than the way we used them here. Actions are similar to mutations, the differences being that:
- Instead of mutating the state, actions commit mutations.
- Actions can contain arbitrary asynchronous operations.
summary:
This is a brief introduction to Vuex. It is intended to introduce you to the basics and get you started. There are still a few concepts missing in this puzzle:
- modules
Mapstate
helpers
you have the basics now. Go build something. You can find the project Demo and Code Source on CodeSandBox.io