Skip to main content
  1. Posts/

The basics of mapState (it's not as hard as it looks)

·790 words·4 mins
Joe Erickson
Author
Joe Erickson
Senior software developer specializing in web development, AI, and helping others learn to code.
Table of Contents

If there is one thing that I see developers looking into Vuex getting hung up on the most, it’s these weird map functions that are in Vuex. The syntax is just so damn weird. What the hell are these ... things doing? Why do I need them sometimes and not others?

The Vuex docs seem to assume a lot here, mainly that you’re already a JavaScript guru and secondly that you’ve heard of a spread operator which is an operator so rarely used in JavaScript that you may have never seen it before.1

To explain all of these concepts, I’m going to pull up a simple example.

Imagine, if you will, that we have a Vue component that shows a user’s name in the UI:

<template>
    <h1>{{ honorific }} {{ firstName }} {{ lastName }}</h1>
</template>

<script>
    export default {
        name: 'show-name',
        computed: {
            honorific() {
                return this.$store.state.honorific;
            },
            firstName() {
                return this.$store.state.firstName;
            },
            lastName() {
                return this.$store.state.lastName;
            }
        }
    }
</script>

And a Vuex store with the following state:

state: {
    honorific: 'Mr.',
    firstName: 'Johnny',
    lastName: 'Bravo'
}

When the Vuex store is passed into the Vue component, the component will use the value from the firstName from the Vuex store as a computed property called firstName. So when the UI references firstName, it will get the value from the store. Same, of course, for lastName and honorific.

This is such a common thing to do that Vuex decided that they would make a helper method to make this easier. If all of your values come from the Vuex store for your component, you can replace all the boiler plate above with this:

<script>
    import {mapState} from 'vuex';
    export default {
        name: 'show-name',
        computed: mapState(['honorific', 'firstName', 'lastName'])
    }
</script>

That’s a lot less typing! But what is it doing?

What’s happening is that mapState() is returning an object that has all of that previous code already filled out. All the functions get set up for us, so all we need to do is pass them straight to computed.

In other words, this:

mapState(['honorific', 'firstName', 'lastName'])

Returns this:

{
    honorific() {
        return this.$store.state.honorific;
    },
    firstName() {
        return this.$store.state.firstName;
    },
    lastName() {
        return this.$store.state.lastName;
    }
}

computed is already expecting an object full of functions, so it takes those and uses them. This is Vuex trying to be helpful! Thanks, Vuex!

But what happens when we have other computed properties? If mapState() is returning an entire object, we aren’t able to do this:

<script>
    import {mapState} from 'vuex';
    export default {
        name: 'show-name',
        computed: {
            fullName() {
                return this.firstName + ' ' + this.lastName;
            },
            mapState(['honorific', 'firstName', 'lastName'])
        }
    }
</script>

mapState() is returning an entire object, so the above code is equivalent to:

<script>
    import {mapState} from 'vuex';
    export default {
        name: 'show-name',
        computed: {
            fullName() {
                return this.firstName + ' ' + this.lastName;
            },
            {
                honorific() {
                    return this.$store.state.honorific;
                },
                firstName() {
                    return this.$store.state.firstName;
                },
                lastName() {
                    return this.$store.state.lastName;
                }
            }
        }
    }
</script>

And, yuk, that’s not right at all. In fact, it won’t even run and you should get a big, ugly error message on the screen. This is because computed is expecting an object with functions, not an object embedded in another object that has functions. That’s just bad syntax.

What we want to do it take those functions out of the object and put them in the computed object.

Well, you can. Modern versions of JavaScript have an operator called the spread operator and it’s that strange ... you see in some of the documentation. Putting ... before the mapState() method says to take each thing in the object returned and put it right here. Rip it out of that object and put it in this one right here. In our example, it turns this:

<script>
    import {mapState} from 'vuex';
    export default {
        name: 'show-name',
        computed: {
            fullName() {
                return this.firstName + ' ' + this.lastName;
            },
            ...mapState(['honorific', 'firstName', 'lastName'])
        }
    }
</script>

Into this:

<script>
    import {mapState} from 'vuex';
    export default {
        name: 'show-name',
        computed: {
            fullName() {
                return this.firstName + ' ' + this.lastName;
            },
            honorific() {
                return this.$store.state.honorific;
            },
            firstName() {
                return this.$store.state.firstName;
            },
            lastName() {
                return this.$store.state.lastName;
            }
        }
    }
</script>

And now we have a valid object of functions.

TL;DR
#

To recap:

If you have no other computed properties, use this:

computed: mapState()

Otherwise, use this:

computed: {
    otherProperty() {
        return 'value';
    },
    ...mapState()
}

That’s it. It is here to make your life easier, not more confusing. Hopefully now, it can do that for you.


  1. It’s also really new. Edge doesn’t even really support it yet. This is why it’s important to use something like Babel that can help these poor, lowly browsers play better with newer syntax. ↩︎

Related

How to query your API using Vuex in your Vue application

·866 words·5 mins
Once you start using Vuex to manage the shared data of your Vue application, it becomes less clear on where or how to call your back-end API. I think everyone starts out making API calls in the created function of your component. But that doesn’t scale past a handful of components. Then, they end up loading in the same data over and over again and each component has it’s own copy, which is impossible to keep in sync. You lose the efficiency of having one part of the system in control of the data when you do that and on a larger application, this very quickly falls apart.

Should you always use getters in Vuex?

·1166 words·6 mins
One of the questions that comes up time and again concerning Vuex is “Do I always use a getter when accessing data? Or can I directly access the raw state?” It’s one of those things you hear about that you should do, but no one seems to really explain why. And do you really need to create a getter for every single piece of data that you put in the store? Isn’t that just a bunch of unneeded boilerplate and duplication?

Loading dynamic images in a Vue Component

·541 words·3 mins
When I first started using Vue, one thing I got continually wrong was how to load an image into a Vue component dynamically. At first, I found that using an absolute URL worked, but that was only useful if I was storing the images on a CDN or other external site. If I included the images in my project, as either lightweight icons or static images, then using an absolute URL, with hostname and all, didn’t really work. What about when I wanted to test some new images locally or on a dev server? Linking directly to the images in production just wasn’t going to cut it.

Understanding data flow in Vuex

·646 words·4 mins
If you’re like me, when you first ran into Vuex, you probably wondered “How the heck does this work?” It’s not immediately obvious how these types of state management systems work, especially if you come from an SQL background. And do I even need it? In fact, the Vuex documentation has a quote that sums it up pretty well: