Understanding ...mapGetters in Vuex

Laurie - Aug 14 '19 - - Dev Community

In vuex there is a helper function that shows up a lot.

computed() {
    ...mapGetters(['currentUser'])
}
Enter fullscreen mode Exit fullscreen mode

When I was first working with it, I had some babel issues preventing me from using ES6 syntax and as a result, I couldn't use the function as intended. Using the workaround helped me understand exactly what this small snippet was doing, so I thought I'd write it up.

As it turns out, there are a couple of things happening here. One is what mapGetters does as a function, and the other is how the spread syntax transforms the result. Let's start by diving into the function itself.

What is mapGetters?

In our example the purpose of mapGetters is to map the result of store.getters.currentUser to the computed attribute currentUser. This allows us to access the result of our getter function like so.

<div>{{currentUser}}</div>
Enter fullscreen mode Exit fullscreen mode

As it turns out, mapGetters can take an object or an array. So this snippet, which is shown above.

computed() {
    ...mapGetters(['currentUser'])
}
Enter fullscreen mode Exit fullscreen mode

Can also be this.

computed() {
    ...mapGetters({currentUser: 'currentUser'})
}
Enter fullscreen mode Exit fullscreen mode

In the first case the name assignment is implicit, in the second it's explicit. But that's the only difference.

Now that we know what the function is doing, let's break it down.

I'm always in favor of having multiple different ways to understand concepts. So, I asked my coworker to read this post and his explanation of what mapGetters was doing went like this.
"The idea is to take a set of variables in your store object in a view controller, and map them to a top-level getter function you can call by name from the two way binding on the html page?"

mapGetters

Let's say that we aren't able to use the function or helper syntax at all. In order to accomplish the same thing, what do we need to do?

We want to call our getter function, return the result and make that result accessible via some sort of name. So we'd do this.

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

We're creating a function and returning the result of our getter function inside of it. The result can be accessed using that function name, as in this example.

<div>{{currentUser}}</div>
Enter fullscreen mode Exit fullscreen mode

Keep in mind that the name we're using is the name of the computed function, NOT the name of the getter method we're calling. Take this example where the names aren't the same.

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

In order to access the result, we'd reference otherName.

<div>{{otherName}}</div>
Enter fullscreen mode Exit fullscreen mode

A lot of this binding comes from Vue. You can read more here.

Deep cut

If you want to really deep dive, here is the source code for mapGetters.

/**
 * Reduce the code which written in Vue.js for getting the getters
 * @param {String} [namespace] - Module's namespace
 * @param {Object|Array} getters
 * @return {Object}
 */
export const mapGetters = normalizeNamespace((namespace, getters) => {
  const res = {}
  normalizeMap(getters).forEach(({ key, val }) => {
    // The namespace has been mutated by normalizeNamespace
    val = namespace + val
    res[key] = function mappedGetter() {
      if (
        namespace &&
        !getModuleByNamespace(this.$store, 'mapGetters', namespace)
      ) {
        return
      }
      if (
        process.env.NODE_ENV !== 'production' &&
        !(val in this.$store.getters)
      ) {
        console.error(`[vuex] unknown getter: ${val}`)
        return
      }
      return this.$store.getters[val]
    }
    // mark vuex getter for devtools
    res[key].vuex = true
  })
  return res
})
Enter fullscreen mode Exit fullscreen mode

There is a lot here, and some of that is to handle the difference in the object versus array input, but the meat of it is in the mappedGetter function. It takes the return object, and using the given key, assigns the result of the associated getter function to the value.

Multiple

Now that we understand the function itself, let's turn our attention to the spread syntax. The key to understanding ...mapGetters really comes from recognizing that the function is designed to handle multiple getter calls at the same time. The single use case we started with still works, but let's look at this.

computed() {
    currentUser() {
         return this.$store.getters.currentUser;
    }
    otherThing() {
         return this.$store.getters.otherThing;
    }
    finalThing() {
         return this.$store.getters.finalThing;
    }

}
Enter fullscreen mode Exit fullscreen mode

We're taking the result of our getter functions and assigning them to function variables we can reference inside our component. It's the same thing we did above, just for multiple getter calls at the same time.

Doing this allows us to render all of these results.

<div>{{currentUser}} {{otherThing}} {{finalThing}}</div>
Enter fullscreen mode Exit fullscreen mode

Now it's easier to explain the ... in front of mapGetters.

Spread Syntax

You may be familiar with ES6 spread syntax. If you want a refresher I wrote a post on it here.

It's a powerful piece of syntax that can do a number of different things. But let's see what it's doing in our example.

We've seen that mapGetters is designed to replace multiple function calls. When it resolves, it returns an object that consists of key-value pairs. Each key is a string name, and each value is the result of the associated getter function call. So really, we're left with something like this.

computed() {
    ...{'currentUser': currentUser, 'otherThing': otherThing, 'finalThing': finalThing}
}
Enter fullscreen mode Exit fullscreen mode

Note that this is not valid JavaScript! It's just an abstraction.

As it turns out, that looks an awful lot like an object literal. And how does the spread syntax handle object literals? It acts as Object.assign().

If you're unclear what that means, it's really accomplishing the following. The spread syntax selects each key-value pair inside the object and pulls it out as a stand-alone object. So each key can be used to reference the value that stores the result, just like we saw before.

<div>{{currentUser}} {{otherThing}} {{finalThing}}</div>
Enter fullscreen mode Exit fullscreen mode

All Together Now

Let's look at our code from the very beginning.

computed() {
    ...mapGetters(['currentUser', 'otherThing', 'finalThing'])
}
Enter fullscreen mode Exit fullscreen mode

Can we see what's happening now? Each string in the array is processed by mapGetters. It takes the getter method of the same name and calls it, then it assigns the result as the value where the key is the original string.

mapGetters returns an object with a series of key-value pairs based on that formula. Then, the spread syntax plucks out each of those key-value pairs to be a stand-alone object that can be referenced directly inside the template code. And that's everything.

Conclusion

That was a lot! And you may never use mapGetters. But it's kind of cool to understand it, right?

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .