DEV Community

loading...

Understanding and using Vuex for the first time

napoleon039 profile image Nihar Raote Updated on ・5 min read

A short intro to Vuex

Vuex is a state management library that can help us manage the state of our application. Instead of multiple components handling state locally and passing it to the required components, we can have Vuex manage state for the entire application in one location for us.

It is best used with Vuejs, but is not limited to it. For example, we can use it with Reactjs if we wish. Vuex is quite powerful.

This short guide explains the basics of using Vuex in a Vuejs application. I will assume you have worked with Vue applications.

State management and the store

Vuex manages state in a central location called a store. Also called a Vuex store, this store contains state as well as some other things that are very useful and make Vuex so powerful. In order to use Vuex in our application, we need to create a Vuex store, register it in the entry file where our main Vue instance is and then we'll be able to use it freely.

Creating a store and registering it doesn't take more than a few steps:

//store.js

import Vuex from 'vuex';
import Vue from 'vue';

Vue.use(Vuex);
export const store = new Vuex.Store();

We have created a Vuex store in a separate store.js file.

//main.js/index.js (or whatever you named the entry file of your application)

import Vuex from 'vuex';
import Vue from 'vue';

//other import statements go here

import App from './App.js';
import store from './store.js';

Vue.use(Vuex);

new Vue({
    store,
    components: {App}
})

Here we registered it with our global Vue instance. Now let's put some state in it:

export const store = new Vuex.Store({
    state: {
        count: 0
    }
})

The Vuex store takes an object as an argument. Inside this object will be the state and other methods. We can retrieve the state using the command: this.$store.state in a Vue component. We can retrieve our count with this.$store.state.count. Here, this.$store refers to the Vuex store.

Getters

If there are any calculations we need to perform on the state before we use it, we can do it in a computed property.

Let's say we want to double the count variable in our state before we display it.

computed: {
    doubleCount() {
        return this.$store.state.count * 2;
    }
}

Although this works in this case, if there are complex calculations to be performed and/or they are needed in multiple components, then it becomes inefficient. We will either have to write duplicate functions for all those components or extract the calculations in a separate function and keep importing it everywhere.

Vuex offers a better alternative - Getters.

export const store = new Vuex.Store({
    state: {
        count: 0
    },
    getters: {
        getSqCount: (state) => {
            return Math.pow(state.count, 2);
        }
    }
})

getSqCount is an arrow function that serves as a getter. All getters receive the state as their first argument from Vuex. We do not need to explicitly pass it when we call them.

computed: {
    sqCount() {
        return this.$store.getters.getSqCount;
    }
}

Note that state and getters are keywords and you cannot use your own names for these in the Vuex store. This also applies to mutations and actions that will follow.

Mutations

We've set state in the store and we've retrieved it. To make changes in our state, we need to use something called a Mutation.

A mutation is similar to an event handler function. The main work of changing (mutating) state will be done inside this function.

export const store: new Vuex.Store({
    state: {
        count: 0
    },
    getters: {
        getSqCount: (state) => {
            return Math.pow(state.count, 2);
        }
    },
    mutations: {
        increment(state) {
            state.count++;
        }
    }
})

The mutation handler function also receives the state as the first argument. As for why I said it's similar to an event handler, it is because of how this mutation is invoked.

Even though it looks like a regular function, we do not call a mutation directly. Think of it more like an event registration. If we want to use the increment mutation we defined inside our store, we have to commit it. We do this in the Vue component.

methods: {
    add: () => {
        this.$store.commit('increment');
    }
}

When this method is executed, it will commit a mutation called increment. Like how an action triggers an event and then the event handler function is executed, when the add() method on our Vue component is executed, it commits the increment mutation which is invoked.

You can also pass some data along with invoking the mutation handler.

methods: {
    add: () => {
        this.$store.commit('increment', 5);
    }
}

This data or payload can be used like any other function argument by our mutation handler.

mutations: {
    increment(state, n) {
        state.count += n;
    }
}

Mutations allow us to update the state. Any components that depend on this state will be updated automatically when this state updates.

But, mutations only allow for synchronous operations. However, asynchronous operations can still be performed with the help of Actions.

Actions

Actions are similar to mutations but they don't change the state directly as mutations do. Instead, they carry out asynchronous operations and then commit mutations when those operations are finished.

We can register an action on our Vuex store in the following way:

export const store: new Vuex.Store({
    state: {
        count: 0
    },
    getters: {
        getSqCount: (state) => {
            return Math.pow(state.count, 2);
        }
    },
    mutations: {
        increment(state) {
            state.count++;
        }
    },
    actions: {
        incrementAsync(context) {
            setTimeout(() => {
                context.commit('increment')
            }, 1000);
        }
    }
})

Here we simulate an asynchronous operation with the help of setTimeout.

Registering an action gives our action handler (incrementAsync) access to a context object.

The context object exposes the same set of methods and properties that the store instance does (this is the this.$store that we used before). We can call context.commit to commit a mutation or even use context.getters to access getters. But, using the context object is not the same as using the store instance.

Since we will be using the context.commit method the most, we can use argument destructuring to unpack it so we can use it easily.

incrementAsync({commit}) {
    setTimeout(() => {
        commit('increment');
    }, 1000);
}

Like how we commit mutations in our Vue components, we dispatch actions.

methods: {
    add: () => {
        this.$store.dispatch('incrementAsync');
    }
}

This works the same way as it did when we used this for commiting our mutation, except this time, we are dispatching an action.

Wrapping up

This should be enough for you to create your first Vuex store, define state, mutations and actions in it. If you need more examples or more information you can read the Vuex docs. They are really good and not too complex. I have also written an article on Vuex before, give it a read if you're interested.

In my next article, I'll explain mapping getters, mutations and actions to computed properties. Thank you for reading!

Feel free to post any questions and suggestions you have in the comments 😃

Discussion

pic
Editor guide
Collapse
omitobi profile image
Oluwatobi Samuel Omisakin

This is a beautiful tutorial. Its easy to follow.

Collapse
steveninc profile image
Steven Benjamin

Why did you use the "=" declaration in an object?

Collapse
napoleon039 profile image
Nihar Raote Author

Fixed it. Thanks for pointing it out! Don't know how I missed something like that 😅