Vue.js explained through pokemon #3 Vuex: state management

This is the third article in my tutorial series that tries to explain Vue.js concepts by recreating the battle scene from Pokemon.

Original tutorial
#1 Single file components
#2 Attacks: $refs, Promises & event bus
#3 Vuex: state management
#4 Damage calculations
#5 Transitions & animation

In this article we’re going to start tackling our state in a different way.

Let’s continue on the event-based implementation of last tutorial:

In the previous articles we talked about isolated state, the data that each component carried with it. This is great for small apps, but when your app starts growing you’ll soon notice your data methods filling up, making it harder to see what data is contained.

Our pokemon app is still small, but once we start adding a more traditional attack system it’ll grow quickly, when we add more pokemon and items it’ll become unmanageable if we keep all our stored data together in a component.

Our main focus is separating the data while still keeping everything reactive and logically stored. Preferably by the means of a common standard, so friends or colleagues can jump in when needed.

Enter Vuex, it’s a state management library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.

This brings a bunch of benefits, one thing you should note is that you won’t be able to change Vuex state directly, unlike local state, to ensure the predictability.

Instead you can commit to the state tree or dispatch .

are functions that directly change a specific part of the state tree.

are meant to commit mutations, they have a more async-nature to them. An action could do a request to your server, take the required data from its response and commit what is needed. In the simplest of uses it can be used to commit multiple mutations.

But wait, there’s something missing, what about computed properties?
Vuex offers getters as an alternative.

Retrieving getters is a little more verbose than computed properties, but I personally don’t mind the tradeoff.

Vuex state changes

Note: As a general rule you should presume that everything you define in a store shouldn’t be accessed directly. There are wrappers in place.

By this I mean that within actions, getters or mutations you shouldn’t try to access the state, getters, mutations or actions through ‘’.

Let’s rethink our app’s state and translate it to Vuex.

New data structure

Let’s begin in , at the moment we have our player and opponent pokemon grouped and hard coded.

We’ll want to change that to having a pokedex and selecting the 2 pokemon from that list. This will also affect the pokemon component.

We’ll also want to separate the computed property into a getter.
We’ll add a mutation that allows us to change a player’s or opponent’s HP.
Lastly, resetting all the pokemon’s HP will have to made into an .

Since we’re extracting our data to a global store, we don’t need to assign a pokemon to a pokemon component, we’ll just retrieve it in the component itself.

In We’ll want to change any statement that tries to change the parent component’s state. This means we’ll have to create a mutator for handling HP changes.

Store definition overview

That’s nice and all, but let’s convert that to actual code.

Let’s pull in vuex

Defining the store

While you’re waiting for that to install let’s create a ‘’ folder, this is where we’ll define our Vuex store.
Within it we’ll also add a ‘’ folder, we’ll make our there:

Feel free to add any pokemon you’d like, I used bulbapedia’s generation 1 chart for stats and attacks.

Let’s go back to the ‘store’ folder and get into defining our state.

We’ll create an file, here we’ll import Vuex and connect it with Vue.

Next we’ll define our store with the needed state, getters, mutations and actions:

Remember when I said nothing should be accessed directly?

Each of our getters and mutation methods will always get as their first parameter. This will always be the most recent version of the state we have defined.

The action that we defined will get the state’s context assigned, this means we actually get a reference to the store. We can access the state through this, as well as other actions, mutations and getters.

If we want to retrieve or change anything we’ll have to do it through that first parameter.

Most of the comments explain enough, however I’m using object destructuring for and.

The reason for this is because mutations only get 2 parameters.
If you want to assign more data you’ll have to group it in an object.
object destructuring is a nice way to show what properties you’re expecting.

Glue it in

Now that we have our store defined, let’s make use of it.

It’s time to open , we’ll import the store and assign it to our root Vue definition.

Once the store has been assigned, you can access it through from the instance it’s been assigned to, as well as every child instance (components).

Accessing the store

We can access anything we’ve defined in our store, an are pretty straight forward, can be called through :

  • state: this.$store.state.property
  • getters: this.$store.getters.property
  • mutations: this.$store.commit(‘mutation’, payload)
  • actions: this.$store.dispatch(‘action’, payload)

Remember, we can’t set values on state and getters, that’s what mutations and actions are for.

You can reference state and getters as shown above, however you could also map them as computed properties, that way you can reference them in your Vue instance like you’re used to doing.

The same is true for mutations and actions, you would reference these in your Vue instance’s methods.

You would end up with something like this:

That works, but Vuex comes with a few helpers that can reduce our code; , and.

These helpers use the same footprint for their parameters, i’ll show you a few examples with and :

I’m using the spread operator here because those functions return an object, both object need to be merged into the computed property object.

Implementing the store

Let’s start by opening App.vue, first let’s get rid of the pokemon attribute on the pokemon components

On to the script tag, let’s import the Vuex helper we need, just like in the previous example.

We’ll start by cleaning our data method and remove everything until , and are left.

We’ll map the needed computed properties ( and ).

While we’re at it, let’s change all the strings that references to literal strings, so they’re cleaner.

We’ll also map the action as in our methods and replace the old code that reset the pokemon’s HP directly.

Lastly we’ll also need to find a way to actually set the player and opponent pokemon.

Vue.js has various lifecycle hooks, we’re already using one, the mounted method on the Vue instance definition, where we set up our event listeners.

We’re going to be using the create hook to setup the pokemon. By doing it in this hook we’ll be sure that the pokemon are set before gets mounted (displayed in the DOM). This also means we’re setting up the global store before it gets accessed through the computed properties we setup.

As you can see in the snippet above, we placed the created method right above our existing mounted method.

Now that our battle stage is ready, let’s dive into .

Let’s go over our to do list:

  • Let’s remove the pokemon prop, it’s not needed anymore
  • We’ll also setup computed properties for the local pokemon (the one this component was set up for) and it’s opponent.
  • The pokemon image :src needs to change since we switched to the structure instead of just a string. We’ll setup a computed property for this.
  • All references to and need to be replaced.
  • Changes made to pokemon HP need to be changed to using the mutator.
  • We’ll also need to change the way the width of the HP bar is calculate since we’re there’s a slim chance the pokemon will have a 100 HP stat.

Under the data method you can see that I renamed the opponent property to , I did this because I’m using state mapping to retrieve the opponent data from the global state under the computed properties.

You can see we map 2 properties from the global state under computed properties:

  1. maps to the pokemon that’s assigned to this component.
  2. maps to the pokemon it’s fighting.

Don’t forget that we defined both these properties in our store to be an object that carries an property (the hp that we reduce with attacks) and a property that stores the pokemon definition.

The image property has also been added to decide on which image to use (an opponent would need the image that shows the pokemon from the front, while the player’s pokemon needs to be shown from the back).

Under methods we map the mutator for easy access and implemented it in the attack method, where we used to directly change the opponent’s HP.

For preparation for the next article I also created a method, currently it just returns the attack’s power, unchanged. But we’ll want to add some calculations later based on stats.

Remember the getter’s we defined in our global store? , .

In pickRandomAttack we’re using the getter we need to retrieve a list of attack names from the local pokemon.

That’s it, we’ve successfully yanked out most of our state and stored it globally.

As always you can find the code on github:

In the next article we’ll focus on how the traditional pokemon game calculates damage based on the pokemon’s stats & type.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store