Vuex best practices

timothyokooboh

timothyokooboh

Posted on April 14, 2021

Vuex best practices

As a Vue.js developer, I have been using Vuex for state management.

Recently, I completed a course on Frontend Masters titled Vuex for intermediate developers. I have also read a couple of useful posts on Vuex written by experienced Vue.js developers.

Therefore, these days when utilizing Vuex in my application, I do my best to follow these patterns/strategies/rules.

A. Always use Mutations to update a value in Vuex state.

Let's say you have something like this in your state:

const state = {
age: 20
}

To update the value of age inside any of your Vue components, never do this:

this.$store.state.age = 40;

Instead, write a mutation that will be responsible for updating the state.

You could write something like this:

const mutations = {
updateAge(state, payload) {
state.age = payload
}
}

Then in your components, you can do this:

this.$store.commit("updateAge", 40)

We can even extend this example further with the next best practice.

B. Let every mutation be associated with an action. Then dispatch these actions inside of your Vue components.

Most experienced Vue developers propose that components should not directly commit mutations. Instead, every mutation should be associated with an action. Then components should dispatch these actions instead of directly committing mutations.

To demonstrate this concept with our previous example, let's create an action:

const actions = {
updateAge({ commit }, payload) {
commit("updateAge", payload)
}

Then inside our Vue components, we will dispatch the action instead of committing the mutation directly.
this.$store.dispatch("updateAge", 40)

C. Keep asynchronous logic inside actions and NEVER inside mutations

Asynchronous logic such as fetching data from an API call should be carried out inside actions and not inside mutations.

This is because each time a mutation updates the Vuex state, it leaves a before and after snapshot of the Vuex state inside of Vue dev tools. This is very useful in debugging because you can always go back in time to check the initial values in the state before the mutation was committed.

But this unique ability of mutations is lost whenever an asynchronous event is fired inside a mutation. Hence it is best to only perform synchronous tasks inside mutations.

Usually, these synchronous tasks only involve updating the value of the state just like we did before:

state.age = payload

D. Actions and Mutations should not be used outside of their core responsibilities.

Because Vuex actions and mutations are simply javascript functions, sometimes we execute logic that should
not really be done inside either of them.

Each time you commit a mutation, it should do only ONE thing: Update the value of the state.

Each time you dispatch an action, it should do one or more of the following:
(i) Fetch data from an API.
(ii) Commit a mutation.
(iii) Dispatch an action.

Each time you find yourself executing logic that falls outside of these core responsibilities, you should consider extracting that logic inside a Vuex plugin.

Thankfully, Vuex plugins are very easy to write. I will show you how.

Use case
Let's say you dispatched an action that fetched data from an API. Then you committed a mutation and passed the API response as a payload to the mutation. Then inside your mutation, you updated the value of the Vuex state with the payload. And in addition, you want to save some values to local storage.

It's best not to write the logic that saves the values to local storage inside of your mutation or action. Instead, you can write a simple plugin to do just that.

How to write a plugin
Let's create a Vuex plugin to save data in Local storage.

Create a folder called plugins, preferably inside of your Vuex store folder. Then create a file called webStorage.js

export default function saveToLocalStorage(store) {
store.subscribe((mutation, state) => {
if(mutation.type === 'updateState' ) {
const payload = mutation.payload;
localStorage.setItem("userId", payload.userId)
localStorage.setItem("username", payload.username)
}
})
}

A Vuex plugin is just a normal javascript function that receives the Vuex store as its only argument.

In the code above, I check if the mutation that was committed is called "updateState". Then I access the payload from the mutation and save the username and userId to local storage.

This logic does not update the Vuex state. Therefore, it's not best practice to place it inside of a mutation.

To use this plugin, we will import it into the Vuex store (index.js file)

store/index.js
import saveToLocalStorage from "plugins/webStorage";

const Store = new Vuex.Store({
plugins: [saveToLocalStorage],
state: {},
mutations: {},
actions: {},
getters: {}
})

Vuex plugins are really powerful and easy to write. You can use them to create powerful notification systems such as notifying a slack channel whenever a particular mutation or action is executed.

E. Don't write redundant code inside Vuex getters.

The below Vuex getter is not really useful and should not have been written in the first place:

const getters: {
getAge(state){
return state.age;
}
}

It is only returning the value of age. In our components, we can conveniently access the value of age without the help of this getter.
this.$store.state.age

Use getters to compute derived values of the state

But let's say we want to check if age is less than 18. Using a getter to compute this value will be very useful.

const getters: {
isUnderAge(state){
return state.age < 18
}
}

Then inside of our components, we can conveniently determine if a user is under age by accessing the value of the getter:
this.$store.getters.isUnderAge instead of writing return this.$store.state.age < 18

That's it, guys! ☺️.
Let me know what you think about these tips. And share the best practices you have been applying inside of your Vuex codebase too.

I must add that these best practices will not be complete without a properly organized Vuex folder structure. But that's a topic for another time.

💖 💪 🙅 🚩
timothyokooboh
timothyokooboh

Posted on April 14, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related