mapState, mapGetters, mapMutations and mapActions with Vue3/Vuex4 and script setup

image

Published here first: https://medium.com/geekculture/mapgetters-with-vue3-vuex4-and-script-setup-5827f83930b4

I just stumbled upon this StackOverflow post while checking out Vue 3 and this new syntax and Composition API and so on and getting to the point where I was ready to add a Vuex store.

In Vue 2…

…populating a component with properties from store getters looked like this:

<template>
  <div>
    {{ count }}
    {{ countIsOdd }}
    {{ countIsEven }}
  </div>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters(['count', 'countIsOdd', 'countIsEven'])
  }
}
</script>

In Vue 3…

…according to the docs, it looks like this and with the <script setup> syntax it looks like this:

<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'

const store = useStore()
const count = computed(() => store.getters.count)
const countIsOdd = computed(() => store.getters.countIsOdd)
const countIsEven = computed(() => store.getters.countIsEven)
</script>

This actually looks totally fine to me but one might argue that computed(() => store.getters. is repeated with every line and that doesn’t look so nice. So…

What I did…

… I added a file called lib.js:

import { computed } from 'vue'
import { useStore } from 'vuex'

const mapGetters = () => {
  const store = useStore()
  return Object.fromEntries(
    Object.keys(store.getters).map(
      getter => [getter, computed(() => store.getters[getter])]
    )
  )
}

export { mapGetters }

And then the component looks like this:

<template>
  <div>
    {{ count }}
    {{ countIsOdd }}
    {{ countIsEven }}
  </div>
</template>

<script setup>
import { mapGetters } from '../lib'

const { count, countIsOdd, countIsEven } = mapGetters()
</script>

Works for me and I felt like sharing this. The same can be applied to actions and mutations of course and to submodules in your store. I just didn’t need that yet so I didn’t implement it.

Well… Ok. 5 Minutes after writing that, I actually added a mapMutations function in lib.js. :D

const mapMutations = () => {
  const store = useStore()
  return Object.fromEntries(
    Object.keys(store._mutations).map(
      mutation => [mutation, value => store.commit(mutation, value)]
    )
  )
}

export { mapGetters, mapMutations }

And in the component:

<script setup>
import { mapGetters, mapMutations } from '../lib'

const { count } = mapGetters()
const { countUp, countDown } = mapMutations()
</script>

I’m not sure if I overlook some unwanted side effects in this but for now, it does what I want.

My final result:

import { computed } from 'vue'
import { useStore } from 'vuex'

const mapState = () => {
  const store = useStore()
  return Object.fromEntries(
    Object.keys(store.state).map(
      key => [key, computed(() => store.state[key])]
    )
  )
}

const mapGetters = () => {
  const store = useStore()
  return Object.fromEntries(
    Object.keys(store.getters).map(
      getter => [getter, computed(() => store.getters[getter])]
    )
  )
}const mapMutations = () => {
  const store = useStore()
  return Object.fromEntries(
    Object.keys(store._mutations).map(
      mutation => [mutation, value => store.commit(mutation, value)]
    )
  )
}

const mapActions = () => {
  const store = useStore()
  return Object.fromEntries(
    Object.keys(store._actions).map(
      action => [action, value => store.dispatch(action, value)]
    )
  )
}

export { mapState, mapGetters, mapMutations, mapActions }

…and in the component:

<template>
  Count: {{ count }}
  Odd: {{ counterIsOdd }}
  Even: {{ counterIsEven }}
  <button @click="countUp">count up</button>
  <button @click="countDown">count down</button>
  <button @click="getRemoteCount('https://api.countapi.xyz')">
    get remote count
  </button>
</template>

<script setup>
import { mapState, mapGetters, mapMutations, mapActions } from '../lib'

// computed properties
const { count } = mapState()
const { countIsOdd, countIsEvent } = mapGetters()

// commit/dispatch functions
const { countUp, countDown } = mapMutations()
const { getRemoteCount } = mapActions()
</script>
H2
H3
H4
3 columns
2 columns
1 column
1 Comment
Ecency