Why and when do you apply Vuex in your project?

At the start of 2017 we tried Vue for the first time. With only some experience in React in personal projects, we took the opportunity to learn and apply Vue in a project we worked on throughout the first half of 2017. After reading the Vue docs and listening to the excellent Vue casts we learned enough to get started on this project.

Currently, we are re-building part of an application that is written in plain JS/jQuery. Although functioning pretty well, the current code is difficult to maintain and new functionality is not easy to incorporate. With our experiences with Vue sofar in mind the question arose whether this new project would need Vuex for state management.

Spoiler alert: it does.

In this post I will explain our reasoning. Let’s start with a quick ‘what to be expected’ from this post. I will discuss

  1. The difficulties you experience when an app grows
  2. The way I think Vuex can solve these difficulties
  3. Some explanation on how ‘things’ (read Vuex store, actions, mutations, getters) are integrated in your app and components.

A simple Vue app without state management

Suppose we write a simple Vue app that generates a page as shown below, for example for a blog:

The list inside the Content component may contain blog posts for example. I assume that  the Main component has all state defined on it and the child components are presentational only: they are passed a bunch of props and render themselves.

So far, so good: all data is flowing down via props, exactly as it is written in the Vue docs.

But what if several components have some user interaction on them? For example

  • Buttons below the list for pagination. Clicking those buttons means fetching a new set of posts from the server and rerender the list with the updated data.
  • Commenting on a post. The comment should be stored in the database on the server and shown on the post page.
  • The footer may have a ‘sign up form’ for our newsletter and upon subscription we need to show a message to the user.

Although these interactions are rather easy to implement you’ll need to create some methods on the Main component and pass these down as props. In addition you may need a global Event bus to simplify communication between parent and child components: The Main component is instructed to listen to specific events the child components may emit. With a lot of user interactions this quickly becomes complex.

These individual interactions may be easy to implement, it is reasoning about the app flow that becomes complicated. People tend to quickly forget which component is emitting which event upon user interaction and what method is responsible for handling the event. Even more so when you are working in a team.

Note: This only holds for interfaces with a lot of interaction! If you are just starting out with a small app, go ahead and use these methods.

So how can Vuex help us?

According to the docs, Vuex is a state management pattern. Vuex extracts? the state of an application into a store and this state can only be changed by mutations. This means we are able to separate the data and the views. However, the two remain coupled by user interactions. A typical Vuex flow may be described as follows:

  1. The state is defined as a single object in the store
  2. The state is rendered following the template(s) of the individual components
  3. A user interacts with the app and dispatches (triggers) an action
  4. The action may do some asynchronous stuff but will eventually commit one or more mutations
  5. A mutation accepts the current state and a payload (some data passed along) and computes the new state
  6. Vue uses the virtual DOM to compare the new and old state and decides whether it should rerender.

This flow is started off with an initial state.

With this in mind, we can overcome the difficulties discussed before. We just need to create the store with an initial state and define actions and mutations. I will not address this part of Vuex as it is discussed in detail in the official Vuex docs. Additionally, here are some links to posts I found when starting off with Vuex implementation.

However, the questions that remain are

  1. How to use this state in our templates and
  2. How do we trigger actions?

Meet mapGetters

In general, when you pass the store to your root Vue component, it is available in all child components by this.$store. Since the state is a property of the store object you can reference it by this.$store.state. So if your state has a property loading: false, you can simply retrieve it from the store and use it in your template as $store.state.loading. Note that in your template you don’t need to use the </span class=”code-courier>this keyword!

However, when your state is a deeply nested object, it becomes tedious to type $store.state.mydata.posts.latest when trying to render the list of latest posts. Meet store getters and mapGetters!

A store getter is simply a “computed property for the state”. Hence, you may define the above example as

latestPosts: state => state.mydata.posts.latest

When you need this computed property in a template inside a component, make sure to

import { mapGetters } from ‘vuex’

and add the getter to your computed properties like so:

computed: mapGetters([latestPosts])

If you already have local computed properties defined on the component, you can use the object spread operator (ES6) to combine them in one object

computed: {
    localComputedProp() {
        ...
    },
    ...mapGetters([
        'latestPosts'
    ])
}

Note that since the object spread operator is currently in stage 3 of ECMAscript (most likely to be included in the next version of ECMAscript) you need a babel plugin to make it work. Otherwise the compiler will throw an error. Install the babel plugin using npm and add the following to your package.json:

“babel”: {
    “plugins”: [“transform-object-rest-spread”]
}

Alternatively use the Object.assign method like this:

computed: Object.assign({},
    {
        localComputedProp(){
            ...
        }
    }, mapGetters([
        'latestPosts'
    ])
}

Hi there, mapActions

mapActions works roughly the same way as mapGetters. The actions are dispatched by the store with $store.dispatch(‘submitComment’)but mapActions simply maps this so you can use submitComment in your template. If we would have 2 actions in the store, toggleLoading and submitComment, you would apply mapActions like this:

import { mapActions } from ‘vuex’

methods: mapActions([
    ‘toggleLoading’, ‘submitComment’
])

Again, combining these store actions with local component methods is easy using the spread object operator or Object.assign as discussed above.

Conclusion

When starting a new project it is good practise to take a moment to think about the flow in your app. Is it a rather static app with nearly no user interactions? Then you probably don’t need Vuex as there won’t be much of a state to handle anyway. Maybe you don’t even need Vue: you might get away with a little plain old JS or even a bit of jQuery!

But is your project an interaction heavy platform with users doing all kind of things? Then sure you will benefit from implementing Vuex. At first it may take some more time to grasp the pattern and syntax. But when implemented correctly, it will lead to a much better maintainable code base that is extendable in the future!


Mijn Twitter profiel Mijn Facebook profiel
Pim Hooghiemstra Webdeveloper and founder of PLint-sites. Loves to build complex webapplications using Vue and Laravel! Latest post
Local Laravel development and SSL on Homestead

Leave a Reply

Your email address will not be published. Required fields are marked *