DEV Community

Nihar Raote
Nihar Raote

Posted on

Mapping for a Vuex beginner

This is a continuation of the article I published previously about using Vuex for the first time. I'll only be talking about mapping your getters, mutations, and actions from the Vuex store to properties in a Vue component.

Why should I map them?

Using Vuex for some time you might be thinking - "I can use a mutation or an action just fine. Why would I map them?". Well, mapping them is very helpful if you're going to use multiple getters, mutations or actions.

Mapping Getters

We use a getter inside our computed property this way:

computed: {
    newValue() {
        return this.$store.getters.newValue;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now how about doing this for 5 getters or 10 getters? It occupies a lot of lines, not to mention having to write a lot of code. You can use the mapGetters helper from Vuex to shorten this. If you want to format the return value of a getter or carry out some other operation with it, you may have to write getters as written above. But if the computed property is going to simply return the value from a getter, it's better to use the mapGetters helper:

import { mapGetters } from 'vuex';

export default{
    computed: {
        ...mapGetters([
            'newValue',
            'getCubeValue'
        ])
    }
}
Enter fullscreen mode Exit fullscreen mode

This would be the same as writing:

export default {
    computed: {
        newValue() {
            return this.$store.getters.newValue;
        },
        getCubeValue() {
            return this.$store.getters.getCubeValue;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

We pass an array to mapGetters and write the name of the getters inside it. You only need to write the name of the getters and then use them in your template. No need to write anything else. If you want to use the getter with a different name, then instead of passing an array to mapGetters, you can pass an object:

import { mapGetters } from 'vuex';

export default {
    computed: {
        ...mapGetters({
            value: 'newValue',
            cube: 'getCubeValue'
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

Without using mapGetters these getters would be written this way:

export default {
    computed: {
        value() {
            return this.$store.getters.newValue;
        },
        cube() {
            return this.$store.getters.getCubeValue;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Mapping Mutations

Like we have done with getters, we can do the same with mutations as well. Similar to mapGetters, we have the mapMutations helper for mapping our mutations.

import { mapMutations } from 'vuex';

export default {
    methods: {
        ...mapMutations([
            'calculatePercentage',
            'incrementAmount',
            'increasePrincipalBy'
        ])
    }
}
Enter fullscreen mode Exit fullscreen mode

The mapMutations helper also supports passing payloads. The last mutation, increasePrincipalBy accepts a payload. Mapping our mutations calculatePercentage, increasePrincipalBy and incrementAmount to the methods property would give the same result as explicitly committing the mutations:

export default {
    methods: {
        calculatePercentage() {
            this.$store.commit('calculatePercentage');
        },
        incrementAmount() {
            this.$store.commit('incrementAmount');
        },
        increasePrincipalBy(amount) {
            this.$store.commit('increasePrincipalBy', amount);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

We can also pass in an object to the mapMutations helper as we did with mapGetters:

import { mapMutations } from 'vuex';

export default {
    methods: {
        ...mapMutations({
            getPercentage: 'calculatePercentage',
            add: 'incrementAmount'
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

This is how we would write the above mapped mutations without mapping them:

export default {
    methods: {
        getPercentage() {
            this.$store.commit('calculatePercentage');
        },
        add() {
            this.$store.commit('incrementAmount');
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Mapping Actions

By now, you must have understood that mapping getters, mutations, and actions is quite similar in syntax. You map getters to computed properties while you map mutations and actions to methods. Mapping actions is similar to mapping mutations, only the helper used is different.

Though I've shown examples of passing an object and an array to the helpers separately, we can use both at the same time:

import { mapActions } from 'vuex';

export default {
    methods: {
        ...mapActions([
            'incrementAsync',
            'getUsers',
            'addUser'
        ]),
        ...mapActions({
            authenticate: 'checkLogin'
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

Here, the addUser action is one that accepts a payload. Now if we write these without using the mapActions helper, they would be written like this:

export default{
    methods: {
        incrementAsync() {
            this.$store.dispatch('incrementAsync');
        },
        getUsers() {
            this.$store.dispatch('getUsers');
        },
        addUser(user) {
            this.$store.dispatch('addUser', user);
        },
        authenticate() {
            this.$store.dispatch('checkLogin');
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

As I mentioned, you can use both ways of passing an object and passing an array in the same method property. I showed this with the actions helper, but it can be done for mutations as well.

Wrapping up

This is it for this article. Mapping your actions, getters and mutations will make your code smaller and save some time writing them. As always, you can learn more from the Vuex docs. If you have any questions or suggestions, feel free to leave them in the comments below.

Top comments (5)

Collapse
 
mattarc profile image
mattarc

Thanks for explaining Vuex Mapping in an easily digestible way. What would be the use-case for mapping mutations like that? Given that mutations should only be committed by actions anyway?

Collapse
 
napoleon039 profile image
Nihar Raote

Let me understand your question properly. Do you mean why is there a need to map mutations in a Vue component since we can just map actions because mutations are, in the end, committed by actions?

Collapse
 
napoleon039 profile image
Nihar Raote

The reason is similar to why we have mutations at all when actions do the same work. If we change the state directly, it will mean the state can be modified/mutated from anywhere, making it hard to find where exactly a particular modification comes from.

Mutations provide a way to centralize all the state mutations. And since the state can't be updated asynchronously, we have to use mutations which are synchronous instead of actions, which can be asynchronous.

Mutations are meant to fulfill a very small part - provide a single place to mutate the state from. Actions, on the other hand, handle all the other logic which can be asynchronous.

Read this answer on StackOverflow about why mutations are needed in spite of having actions.

I hope this answers your question. If you have anything else to ask, feel free to ask away πŸ˜ƒ

Thread Thread
 
mattarc profile image
mattarc

Thanks for the response, Nihar. I understand the need for mutations in the way that as you say we have asynchronous actions for synchronous mutations. But essentially, yes you have hit the nail on the head in your first reply to my comment,
"why is there a need to map mutations in a Vue component since we can just map actions because mutations are, in the end, committed by actions?"
This I still don't understand. It seems to me that a mapped action would commit the mutation in the store; there would never be a need to call a mutation from a mapping?

Thread Thread
 
napoleon039 profile image
Nihar Raote

I honestly haven't found an answer to it as well. It's like no one else wonders why we need to commit mutations in the store when we can dispatch an action for it. But since there's an example for mapping mutations in the component in the Vue documentation, there should be a reason.

Though I haven't seen this reasoning anywhere I think it's because an action is expected to do some asynchronous stuff along with committing mutations. So maybe a use case for committing mutations in the component is when actions do more than just commit mutations and we only need to commit a mutation and not the other stuff?

I'll try to see if there is an answer anywhere like Stackoverflow or ask on Twitter if I still can't find one.