Store Objects for Vuex

AuthorMáximo Mussini
·4 min read

Vuex is a state-management solution that integrates nicely with Vue components.

When starting to use Vuex, one quickly realizes that it's easier to manage and understand the state of a large application when the state is split between different modules in the Vuex store.

Unfortunately, working with modules involves using a namespace string to access state and getters, which makes typos hard to detect, and can quickly become cumbersome if using constants or a similar approach to avoid duplication.

Another weak spot is that dispatching actions feels very unnatural, and also requires specifying the namespace string as a prefix for the action name.

A smoother experience 🥃

The vuex-stores library helps you avoid these shortcomings by providing light wrappers around individual Vuex store modules, which I've dubbed "store-objects".

Store objects address these issues by allowing access to state and getters as properties, and dispatching actions easily by using plain method calls.

As a result, it's possible to leverage all the goodness in Vuex, using an elegant and convenient API.

Conventions 🔤

In order to organize these store objects, an approach that works nicely is to:

  • Create one file per store module under a stores directory.
  • Always use Store as a suffix for the file name (ModalsStore).
  • Add a @stores webpack alias to make them convenient to import.

Let's take a look at an example usage:

<script>
import ModalsStore from '@stores/ModalsStore'

export default {
  name: 'ModalManager',
  computed: {
    // `mapState` and friends are available to inject state or getters into the
    // template, without the need to specify the namespace string.
    ...ModalsStore.mapState('modals'),
  },
  beforeMount () {
    // closeAllModals is an action, and it will be dispatched every time the
    // route changes, to hide any open modals.
    this.$router.afterEach(ModalsStore.closeAllModals)
  },
  methods: {
    // Actions are available as methods, notice the lack of boilerplate to
    // inject it in the component using `mapActions`.
    onModalClose (modal) {
      ModalsStore.removeModal(modal)
    },
    // NOTE: A shorter version would be `onModalClose: ModalsStore.removeModal`
  },
}
</script>

<template>
  <div class="modal-manager">
    <component
      :is="modal.component"
      v-for="modal in modals"
      :key="modal.id"
      v-bind="modal.attrs"
      @modal:close="onModalClose(modal)"
    />
  </div>
</template>

As seen in this short example, state, getters, and actions can be easily injected in a component using map helpers to make them available in the template.

Actions can be dispatched by simply calling a method, which is closer to how they are defined, and feels very natural. Typos in action names are prevented, since a method call would fail if the name is not correct (instead of being ignored)

The namespace string becomes an implementation detail which is transparent to the user, without having to use cumbersome manual techniques (such as constants) to avoid duplicating the module name all over the codebase.

Because incorrect ES6 imports provide clear errors, typos in the store name can be detected at compile time, and refactoring becomes a lot easier (usually as simple as search and replace).

There are more code samples available in the documentation 📖

Code Splitting ✂️

Store modules in Vuex can be registered dynamically, so internally vuex-stores leverages registerModule to add a new module when the store object is imported.

This means that store objects don't need to be imported up-front when initially defining the Vuex store, playing nicely with apps that do code splitting, since the code associated to a store object will only be loaded if needed.

As a result, the initial setup of the Vuex store will be lighter, and by using the conventions described above, code loading and execution will be optimized.

Summary

vuex-stores provides a simple way to work with Vuex modules by allowing you to define store objects to focus on one module at a time, making it more enjoyable to leverage Vuex.

The API is convenient and easy to learn, prevents mistakes such as typos, and works nicely when used in conjunction with ES6 modules, making the code easier to reason about and refactor.

Also, it's a lot of fun, give it a try! 😃