How to use Vuex mappers inside Vue Composition API?

David Meir-Levy
Israeli Tech Radar
Published in
4 min readMar 2, 2020

TL;DR — use npm package vuex-composition-helpers .

Lately, I was playing around with the idea of abandoning Vuex in one of my projects at Greenpress, and after a hackathon I attended, I found a very simple way to use the Composition API as a State management.

After that great experience, I wanted to implement the compositions approach on a frontend project that used Vuex as its state management system.
Because there were many Vuex store modules inside this project, I couldn’t just leave Vuex for good in one single commit, and it had to be done incrementally, step by step so that the code would support both Composition API and Vuex (and I often used createNamespacedHelpers of Vuex, to make dispatches/commits/getters more elegant to read).

A closer look into a Vuex module folder

let’s say we have a Vuex module.
In my project, each module has a bunch of files:

Those files represent the whole cycle of the module behavior.

  1. The component dispatches Actions.
  2. Actions commit Mutations.
  3. Mutations mutate the Module State.
  4. Getters get changed after watched values on the state have changed.
  5. A component reads the State and Getters as computed values (read-only).

Ok, so what’s missing?

Using the Vuex store is available from several entry points:

Option 1: Import your whole store directly and use it:

import store from './my-store'store.dispatch('myModule/myModuleAction')store.myModule.state.blabla

Option2: From a component lifecycle:

export default {
mounted() {
this.$store.dispatch('myModule/myModuleAction')
}
computed: {
items() {return this.$store.myModule.state.items}
}
}

Option 3: UsingcreateNamespacedHelpers :

import {createNamespacedHelpers} from 'vuex'
const {mapActions, mapState} = createNamespacedHelpers('myModule')
Vue.component('my-component', {
methods: mapActions(['myModuleAction']),
computed: mapState(['items'])
})

The third option is considered more elegant because it separates between mentioning the module name and using the actual actions or getters inside a relevant component.
With that, you can gain 3 benefits:

  1. You don’t need to know the actual module separator char (“/”) and mention the whole path by yourself every time you use a getter/state/action/mutation.
  2. You get a mechanism that builds methods and computed values for free. Those can be injected directly to your component, without the need to implement them repeatedly (you put your trust in the Vuex official helpers to write it for you).
  3. Less prone to mistakes. The code becomes more readable to other teammates and promotes reusability

What are the obstacles to make Vuex work with the Composition API?

The first important obstacle is that we don’t really have this as a context inside our setup method, so using this.$store is not really an option.
However, you have other components inside your context (the second argument of the setup method): parent and root. At least one of them (the root for sure) have the $store as a property, so you can do something like (less recommended):

setup(props, context) => ({
blabla: ref(context.root.$store.myModule.state.blabla)
})

Or (the recommended option):

setup(props, context) => ({
blabla: computed(() => context.root.$store.myModule.state.blabla)
})

You still need to manage full direct paths to store/actions inside your component.
Isn’t it one of the main reasons we wanted to move into Composition API?

[OPINIONATED] Key Concepts for Compositions

  1. A Composition managed inside a separated file holds its own specific logic, methods, and values.
  2. A Stateful Composition should be aware of the state it manages, and refer to it.
  3. Avoid re-generating functions as possible, especially in stateful compositions, in which you can share functionality for the same state.
  4. A Composition was made first to create logical blocks of functionality inside components, and second — to reduce code duplications.
  5. If it’s not elegant and easy to maintain or to understand — you’re doing something wrong.

Do it once, do it good

The code example above (using the root component) is forcing you to inject the global state of the application to your composition function, to setup the composition for the component.
In this case, your composition isn’t stateful and it cannot manage the state, and furthermore, it exposes the component to the specifics of the global state, where it is managed, etc.

I wanted to use the createNamespacedHelpers to get the mappers only once inside my compositions files, so I can export those methods once for every time a component asks for it, instead of re-generate the methods every time.

Ideally, my composition file should have looked like this (IT DOESN’T WORK WITH THE OFFICIAL VUEX HELPERS):

const {mapActions, mapState} = createNamespacedHelpers('myModule')export const actions = mapActions(['actionA', 'actionB'])export const { dataList } = mapState(['dataList'])

If it had worked (It wouldn’t, that’s why I recommended vuex-composition-helpers), I could use those actions and getters inside my component’s setup method, like this:

export default {
setup() => ({
...actions,
list: dataList
})
}

Let me try and propose another solution

Let’s consider a composition file structure.
You can still choose this version:

import store from './my-store'
export function myModuleAction(payload) {
store.dispatch('myModule/myModuleAction', payload)
}
export function useThisComposition(){
return {
dataList: computed(() => store.myModule.state.dataList)
}
}

Or you can try vuex-composition-helpers:

import { wrapStore } from 'vuex-composition-helpers';
import store from './my-store';
const { createNamespacedHelpers } = wrapStore(store);
const { useState,
useGetters,
useActions,
useMutations
} = createNamespacedHelpers('myModule')
export { useState, useActions };

As an alternative, you can try to manage your state using Vue.observable() and the composition API instead of using any state management system, as I described in my previous post.

--

--