loading...
Cover image for Managing State with Vuex - the Guide I Wish I'd Had

Managing State with Vuex - the Guide I Wish I'd Had

decoeur_ profile image Lauri Hiltunen ・5 min read

TL;DR: see the vuex flow and/or skip to see the code.

Frontend apps have been growing more and more feature-rich in recent years. "It's for the web" is not a proper reason to deny desktop-like feature requests anymore. At the same time frontends are switching from the traditional MVC model to a more componentized structure, and the need for a solid state management pattern has emerged. After all, the components interacting with each other is a vital part of any bigger app.

Flux is a design pattern released by Facebook, created to structure client-side, component-based applications. There are many implementations of the Flux-pattern but in this post we're going to focus on one: Vuex. This is the guide I wish I'd had when I first started reading about state management with Vuex. There will be code!

Concepts

The key concepts with Vuex are: the state, actions, mutations and getters. The state object contains the application state, and is shared with all the components. Mutations change the state - and they are the only way to change it. Actions commit mutations, the key difference being that mutations can not be asynchronous. We should invoke async actions which commit mutations when the async code has completed. All state mutations must be synchronous! Finally the getters return specific parts of the state for components to use.

 

The flow in a single image

The flow of data inside store: components create actions which commit mutations, mutations alter the state and getters return parts of it.

You can choose not to use some of the steps described above, but for the sake of completeness I'll go through the flow as it's designed to be used.

The sample app

We're going to take a look at some code, which creates the store for one property, mutates it and returns it for components. The sample app is a concept of an activity calculator of some sorts. The basic idea is that you'll select an exercise you're working with and then add the amount of that exercise done, like stairs climbed, the distance you've ran or the pushups you've done. The app for this example consists of two components: one that selects the exercise and the other one that uses the selected exercise and allows you to mark the "reps" you've accomplished and send the data to a backend service for further processing.

Bring on the code

Let's get going with the code - I did use the vue-cli simple webpack setup to enable ES6 features. First of all, let's create the Vuex store.

The state inside store.js

The state inside the store is just another object, it can contain anything you want.

//store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const state = {
  selectedExercise: undefined
}
// ...
We have the state object in store.js, it contains the selectedExercise which we'll mutate in a little while.

Actions

Then we're having the action methods, they get the context as their first parameter and the possible payload as the second param. This action creates a mutation by calling context.commit with the name of the mutation and passing possible payload to go with it.

//store.js
const actions = {
  selectActiveExercise(context, exercise){
    console.log('selecting exercise action, ' + exercise.name)
    context.commit('selectExercise', exercise);
  }
}
// ...
Actions commit mutations

Mutations

And then there are the mutations. Mutations get the state as first parameter and an optional payload as second. The action from previous step committed a mutation which calls the selectExercise method which in turn changes the state for real.

//store.js
const mutations = {
  selectExercise(state, exercise){
    console.log('selecting exercise mutation, ' + exercise.name)
    state.selectedExercise = exercise
  }
}
// ...
Mutations altering the state

Getters

The last missing part - the getters exposed by the store. You can call the selectedExercise getter from any of your components and it'll return you that specific portion of the state.

//store.js
const getters = {
  selectedExercise(state){
    console.log('getting selected exercise')
    return state.selectedExercise
  }
}
// ...
Getter returning a part of the state

Exporting the Vuex store

Build up the store and export it so we can use it.

//store.js
export default new Vuex.Store({
  state,
  actions,
  mutations,
  getters
})

Import the store and use it in your app

Initialising the app with the store.

// your app.js/main.js, some code omitted
import store from './store/store.js'

new Vue({
  el: '#app',
  store: store,
  router: router,
  template: '<App/>',
  components: { App }
})
We're importing the store from newly-created store.js and assigning it as a store to our app.

Using the store inside components

Running actions and mutating the state

Now that we've set up the store, we can use it in our components. First of all the exercise-selector component, which triggers the action that selects the active exercise for our context by running the select exercise action which in turn runs the mutation that commits the change to state.

import { mapActions } from 'vuex'
export default {
  name: "exercise-selector",
  methods: {
    ...mapActions( {
      selectActiveExercise: 'selectActiveExercise'
    } )
  }
// code omitted...    
}
Exercise selector vue component, it maps the selectActiveExercise action for use in our Vue component
<template>
    <li class="exercise-row" @click="selectActiveExercise" role="button">
      <div class="name">{{ exercise.name }}</div>
      <div class="pointsPerUnit">
        <span>{{ exercise.pointsPerUnit }} points per {{ exercise.unit }}</span>
      </div>
    </li>
</template>
Using the mapped "selectActiveExercise" method inside the template

Getters

After taking care of mutating the state, we're mapping the getters defined in the store to our other component. This effectively creates a computed getter method with the name "selectedExercise" for our component.

import { mapGetters } from 'vuex'
export default {
  name: "exercise-input",
  computed: {
  ...mapGetters([
    'selectedExercise'
   ])
  },
//...    
}
Mapping the getter "selectedExercise" from vuex store for use as computed properties

When the getter is in our component's context, we can use it in our template as follows.

<div v-if="selectedExercise">
  <h2>Add exercise</h2>
  <div class="input-container">
    <div class="input-selected-name">{{ selectedExercise.name }}</div>
    <in-put class="input-number" v-on:keyup.enter="addExercise" type="number" placeholder="0" v-model="units"></in-put>
    <div class="input-unit">{{ selectedExercise.unit }}</div>
    <div class="input-details">Points {{ selectedExercise.pointsPerUnit}} per {{ selectedExercise.unit }}</div>
    <button @click="addExercise">Add to your exercises record<span class="forward"></span></button>
  </div>
</div> 
Mapping the getter "selectedExercise" from vuex store for use as a computed property

So we are using the mapped getter method inside our template. This effectively gets the data from store and is updated automatically when any component commits the mutation that changes the selected exercise.

And that's it, Vuex with a couple of lines of code.

Afterword

I got into Vuex a couple of weeks ago during a get-together with my colleagues. At first all the talk about actions and mutations seemed a bit confusing and complicated, but to see it in a few lines of code makes it quite clear and understandable. And in the end using centralized state does make application development easier as the size of the application gets bigger. When the state changes are reactively rendered in every component, you can focus on the key features which alter the state instead of doing something like emitting events or updating your views manually.

I like it, it beats all the manual scripting and event-based solutions I've seen before. By a mile!

Discussion

pic
Editor guide
Collapse
ndiecodes profile image
Ndifreke Friday

This was very helpful, Thank you.

Collapse
developersubash profile image
developer-subash

Thank you so much it was great article but now i am using vue cli 3 and use multiple vuex store in vue application but i am unable to do so can you please help me

Collapse
decoeur_ profile image
Lauri Hiltunen Author

What is the problem with it? Maybe using multiple Vuex modules should be a post of it's own.

Collapse
developersubash profile image
developer-subash

Yup i am new to Vuejs and multiple Vuex modules it was much more confusing to me and i have tried abut it cannot work on my case can you have any solution

Thread Thread
decoeur_ profile image
Lauri Hiltunen Author

I think the official documentation is quite simple, have you checked this out: vuex.vuejs.org/guide/modules.html

I'm afraid that if I don't see the code nor the errors you're facing I don't know what you're struggling with. If you don't have the need for modules at first, try sticking with just one central Vuex store and continue from there when the need arises.

Collapse
iamdanielwalter profile image
Daniel Walter

Is it possible to call a getters method to get a particular value from the state, after an action has been carried out. Usually getters are called from computed properties which initially will hold the empty value of the state before a particular action will be carried out which in turn will call a particular mutation.

Collapse
steveninc profile image
Steven Benjamin

Do you have a repo with the completed code base?

Collapse
decoeur_ profile image
Lauri Hiltunen Author

Hi Steven, thanks for asking. Unfortunately I don't have it on me anymore since it originally was just a spike on how Vuex works.