Vue 3 & Vuex 4: useStoreModule composable
Tags: vue, typescript, javascript
Published: 2020-07-28 14:50:13

Now that Vue 3 and Vuex 4 official releases are around the corner, I took this opportunity to start rewriting one of my side projects in (you guessed it) Vue 3!

Vue 3 introduces the [Composition API](https://v3.vuejs.org/api/composition-api.html) (which is also available in Vue 2 as a [plugin](https://github.com/vuejs/composition-api)) which opens up a whole range of possibilities. It's probably worth reading the docs first to be able understand why composables are nice and super powerful but without wasting anymore of your time, let's get into it.

This is inspired by a [Github issue](https://github.com/vuejs/vuex/issues/1695) on the Vuex repo.

---

#### The problem

Vuex supports the idea of splitting your store into [modules](https://vuex.vuejs.org/guide/modules.html) which is something you almost definitely want to do if you're working on a large project as it makes your state more manageable and easier to work with. Along with modules, you almost always [namespace](https://vuex.vuejs.org/guide/modules.html#namespacing) them to avoid any naming clashes by containing the module in a namespace.

In order to access any of the properties of your module in a Vue component, you will either have to use the global `$store` property on `this` object or *map* them as computed properties or methods which would look something like this:
```html
<template>
  <h1>{{ greeting }}</h1>
</template>

<script>
import { createNamespacedHelpers } from 'vuex';

const { mapState, mapActions } = createNamespacedHelpers('my-module');

export default {
  computed: {
    ...mapState({
      greeting: state => state.greeting
    })
  },
  methods: {
    ...mapActions([
      'setGreeting'
    ])
  },
  mounted () {
    setTimeout(() => {
      setGreeting('Hi there 👋');
    }, 5000)
  }
}
</script>
```

It's not too bad for a SFC component but you have to be very explicit in what you want to return which doesn't make this a nice DX.

The documentation for Vuex 4 is still a little vague and I didn't have enough time to investigate how to properly use Vuex in a functional component but it'll be very similar to the SFC one 🤷‍♂️

#### The solution

To fix this problem, I created a little composable which hooks into Vuex's internal properties and allows you to interact with your `state`, `actions`, `mutations` and `getters`.

It can be used like this if you had one store:

```tsx
import { defineComponent } from 'vue';
import { useStoreModule } from './composables';
import { State, Actions } from './state';

const Greeting = defineComponent(() => {
  const { state, actions } = useStoreModule<State, Actions>('my-module');

  setTimeout(() => {
    actions.setGreeting('Hi there 👋');
  }, 5000);

  return () => <h1>{state.greeting}</h1>;
});

export default Greeting;
```

If you had multiple stores, `useStoreModule` accepts a second argument which would be the name of the store. So like this: 
```tsx
import { useStoreModule } from './composables';
import { State } from './state';

const { state } = useStoreModule<State>('my-module', 'store-name');
```
--- 
The `useStoreModule` composable is available as a [gist](https://gist.github.com/baryla/dab7ebb745623dc70ff2d21f474591a1) 🚀


Let me know if you find it helpful 💥 👊