State Management with Vue / Vuex

Nick Pate
5 min readSep 17, 2018

In part I of Reusable Vue / Vuetify components we made a reusable component by utilizing Vue’s props. Once an application becomes more complicated, passing props and emitting back to parents and grandparents can quickly become difficult to maintain. Vuex is the way to manage state in your Vue application, so lets explore integrating Vuex into our simple component.

Add Vuex to the project

[nick_pate@linux my-chip]$ yarn add vuex
yarn add v1.9.4
[1/4] Resolving packages...
[2/4] Fetching packages...
......
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ vuex@3.0.1
info All dependencies
└─ vuex@3.0.1
Done in 1.67s.

Create folder structure for state

We have created a directory named store, within it files named: GameStoreModule.js, index.js and mutation-types.js. Lets go through what these do.

mutation-types.js

Used later as constants for Vuex Mutations

GameStoreModule.js

We will use most of Vuex’s Core Concepts including State, Getters, Actions, Mutations and Modules.

First, import mutation types. By using constants instead of strings will can stay organized once our application increases in complexity.

In the constructor we are setting a class variable called namespace and setting it to true. This will enable Vuex’s modules feature and allow us to break up our state into smaller modules. Using this, we can encapsulate functionality instead of having all of the state in the global namespace.

Chrome Extension Vue.js devtools

Chrome has an excellent Vue/Vuex debugger. Notice the game/ prefix. Without namespaces all state would be in the global context. Imagine having to come up with unique names for all of your state. As an example, say we wanted to track archived games. You could create another ES6 class named GameArchiveStoreModule. This could contain the same getters and state variable names as GameStoreModule, but they would be prefixed with archive instead of game.

this.state

Define what we want to keep track of.

this.getters

We can access our state through Getters. Getters can be used to calculate computed values. In more complicated components, you may want to present your state in a different way that it is stored. A Getter would be the perfect place for that. I prefer to always access Vuex state through getters.

this.actions

Actions are what we use to set state. They are similar to mutations, although actions call a method named commit instead of setting state directly. Actions are the places where you may want to call out to an API and get data, then commit it to state. Notice we are using ES6 destructing to be able to use commit as a method call.

this.mutations

Vuex’s documentation says: “The only way to actually change state in a Vuex store is by committing a mutation.” Notice the first argument to our mutations is state. The second argument is whatever we passed in through our actions. It could be an object, array, string… etc.

index.js

Import our GameStoreModule, tell Vue we want to use Vuex and create a new Vuex Store with modules configured. The name you pass into the module will be the name of your store. Remember to open main.js and add the store to our vue instance.

Import Store, add to new Vue instance

Now we have our building blocks in place. Lets use it!

Remember, our previous component passed in selected and available titles into MyChip’s props.

Previous HelloWorld Component

Lets change this to use Vuex!

First, I removed v-model and available props from the MyChip component. On Line 15 we imported the Vuex mapActions helper. By doing so we can access our Vuex Actions via this. Under methods, I used mapActions in two different ways. The first mapActions is how you would use the exact defined Vuex Action we created in GameStoreModule.js. The second mapAction example demonstrates how to set a custom this variable name for a defined Action. This is useful when you have overlapping action method names. Notice in both cases we are accessing the game module or namespace. When the component mounts I am calling our actions to set state instead of passing props to MyChip.

VueTools showing that our state is populated on component mount.

Now we need to change our MyChip component to use Vuex instead of relying on props. In most cases I would not use Vuex in a reusable component because that breaks the Single Responsibility Principle and also couples the two components. Alternatively I would use getters in the parent component as the props for MyChip. For the purpose of demonstrating Vuex, this is a fine approach.

Here is our previous MyChip Component.

Old MyChip Template Section
Old MyChip Script Section

The Script section is going to change in a number of ways. First, we will remove the value and available props. We will be getting those from Vuex instead. Next, we will import mapGetters, mapActions from Vuex and create a computed section that generates our getters. After that, we will add a mapActions section, update our remove method and create an onChange method.

We are using our first getters here, they will be passed to Vuetify’s v-combobox. Also, notice we changed the remove method to call our Vuex action instead of emit. Make sure not to try to mutate a getter, hence the object.assign.
Notice, we are passing our getter getSelected and getAvailable to v-combobox. Anytime our Vuex state changes, this component will re-render with the new data.

A few things to note here. We changed v-model to :value. We were previously using v-model to setup two way binding, but that will cause Vuex state mutation issues. To handle the changes we setup an onChange method and just set our Vuex state with an action.

Were done!

Whats next? What would you improve on? Maybe create a generic base store class? How could we keep MyChip from using Vuex and maintain its re-usability?
Hint: Keep the props and emits in MyChip, but pass in getters.

Try it out and thanks for reading!

View the source code.

--

--

Nick Pate

Whether its running an engineering team or heads down coding I thrive at solving problems you can't Google.