Introduction to State Management with Vuex
Tags: vue, javascript, beginners
Published: 2020-03-04 17:26:32

---
title: Introduction to State Management with Vuex
published: true
description: The vuex library enables storing shared state in Vue apps. In this tutorial, we explore Vuex actions, composing actions, and modules.
tags: vue, javascript, beginners
canonical_url: https://blog.jscrambler.com/introduction-to-state-management-with-vuex/
cover_image: https://blog.jscrambler.com/content/images/2020/03/blog-post-state-management-vuex-1.jpg
---

Any Vue app that is more than a few components big is going to have shared state.

Without any state management system, we can only share state between parent and child components.

This is useful, but it's limited. We need to be able to share state regardless of the relationship of the components.

To do this in a Vue app, we can use [Vuex](https://vuex.vuejs.org/). It’s the official state management library for Vue apps.

In this article, we'll look at how to share state with Vuex.

## Getting Started

To get Vuex into our Vue app, we have to include the Vuex library. Then, we create a Vuex store with the initial state, mutations, and getters.

For example, we can create a simple store for our Vuex to store the `count` state and use it in our app as follows:

`index.js`:

```javascript
const store = new Vuex.Store({
  state: {
    result: {}
  },
  mutations: {
    fetch(state, payload) {
      state.result = payload;
    }
  },
  getters: {
    result: state => state.result
  }
});

new Vue({
  el: "#app",
  store,
  methods: {
    async fetch() {
      const res = await fetch("https://api.agify.io/?name=michael");
      const result = await res.json();
      this.$store.commit("fetch", result);
    }
  },
  computed: {
    ...Vuex.mapGetters(["result"])
  }
});
```

`index.html`:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vuex"></script>
  </head>
  <body>
    <div id="app">
      <button @click="fetch">Fetch</button>
      <p>{{result.name}}</p>
    </div>
    <script src="./index.js"></script>
  </body>
</html>
```

In the code above, we added the `script` tags for Vue and Vuex in `index.html`.

Then, we create a div with ID `app` to house our Vue app. Inside the div, we add the button to call a method to increment the `count` state.

Then, we show the `count` in the `p` tag.

In `index.js`, we write the code for the Vuex store by writing:


```javascript
const store = new Vuex.Store({
  state: {
    result: {}
  },
  mutations: {
    fetch(state, payload) {
      state.result = payload;
    }
  },
  getters: {
    result: state => state.result
  }
});
```

The code above gives us a simple store that stores the `result` state initially set to an empty object.

Then, we added our `fetch` mutation in the `mutations` section, which takes the `state` object that has the Vuex state. We then get the data from the `payload` and parameter and set the `state.result` to `payload` each time the `fetch` action is dispatched.

Also, we added a getter for the `result`, which is a function that takes the `state` of the store and then returns the `count` from it.

After we create the store, we use it in our Vue app by adding the `store` property into the object we passed into the `Vue` constructor.

Additionally, we get the `result` state in our Vue app by using the `mapGetters` method, which maps our getter to the `result` computed property in our Vue app.

Then, we wrote a `fetch` method, which references the `$store` instance and calls `commit` with the mutation name passed in.

We passed in the `’fetch’` string to commit the `increment` mutation.

Finally, our button has the `@click` prop and it's set to the `fetch` method.

In the end, when we click on the Fetch button, we should see ‘michael’ displayed from the store’s data as the API data is fetched.

We can also pass in the action type and payload as an object into the `commit` method as follows:

```javascript
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state, payload) {
      state.count += payload.amount;
    }
  },
  getters: {
    count: state => state.count
  }
});

new Vue({
  el: "#app",
  store,
  methods: {
    increment() {
      this.$store.commit({
        type: "increment",
        amount: 2
      });
    }
  },
  computed: {
    ...Vuex.mapGetters(["count"])
  }
});

```

In the code above, we changed `mutations` to:

```javascript
mutations: {
    increment(state, payload) {
        state.count += payload.amount;
    }
},
```

Then the `increment` method is changed to:

```javascript
increment() {
    this.$store.commit({
        type: "increment",
        amount: 2
    });
}
```

Mutations can only run synchronous code. If we want to run something asynchronously, we have to use actions.

Vuex can't keep track of mutations that are asynchronous since asynchronous code doesn’t run sequentially.

We can also use the `mapMutations` method like the `mapGetters` method for getters to map mutations.

To use the `mapMutations` method, we can write the following code:

`index.js`:

```javascript
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state, payload) {
      state.count += payload;
    }
  },
  getters: {
    count: state => state.count
  }
});

new Vue({
  el: "#app",
  store,
  methods: {
    ...Vuex.mapMutations({
      increment: "increment"
    })
  },
  computed: {
    ...Vuex.mapGetters(["count"])
  }
});
```


In the code above, we mapped the `increment` mutation to the `increment` method in our Vue app with the following code:

```javascript
...Vuex.mapMutations({
    increment: "increment"
})
```

It takes an argument for the `payload` as we can see in the `@click` listener of the Increment button.

## Actions
Actions commit one or more mutations in any way we like.

We can add a simple action to our store and use it as follows:

`index.js`:

```javascript
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state, payload) {
      state.count += payload;
    }
  },
  getters: {
    count: state => state.count
  },
  actions: {
    increment({ commit }, payload) {
      commit("increment", payload);
    }
  }
});

new Vue({
  el: "#app",
  store,
  methods: {
    ...Vuex.mapActions(["increment"])
  },
  computed: {
    ...Vuex.mapGetters(["count"])
  }
});
```

<a href="https://blog.jscrambler.com/how-to-protect-your-vue-js-application-with-jscrambler/?utm_source=dev.to&utm_medium=referral" target="_blank"><img alt="Protect your Vue App with Jscrambler" src="https://media.jscrambler.com/blog/V5-Vue.jpg"></a>

In the code above, we defined the `increment` action in the Vuex store as follows:

```javascript
actions: {
    increment({ commit }, payload) {
        commit("increment", payload);
    }
}
```

Then we called `mapActions` in our Vue app as follows:

```javascript
methods: {
    ...Vuex.mapActions(["increment"])
},
```
The `increment` method, which is now mapped to the `increment` action, still takes the `payload` that we pass in, so we can pass the `payload` to the `commit` function call in our `increment` action, which is then passed into the `increment` mutation.

We can also write an asynchronous action by returning a promise as follows:

`index.js`:

```javascript
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state, payload) {
      state.count += payload;
    }
  },
  getters: {
    count: state => state.count
  },
  actions: {
    increment({ commit }, payload) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          commit("increment", payload);
          resolve();
        }, 1000);
      });
    }
  }
});

new Vue({
  el: "#app",
  store,
  methods: {
    ...Vuex.mapActions(["increment"])
  },
  computed: {
    ...Vuex.mapGetters(["count"])
  }
});
```

In the code above, we have:

```javascript
actions: {
    increment({ commit }, payload) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          commit("increment", payload);
          resolve();
        }, 1000);
      });
    }
}
```

which returns a promise that commits the `increment` mutation after a second.

## Composing Actions
We can compose multiple actions in one action. For example, we can create an action that dispatches multiple actions as follows:

`index.js`

```javascript
const store = new Vuex.Store({
  state: {
    dog: {},
    breeds: { message: {} }
  },
  mutations: {
    setDog(state, payload) {
      state.dog = payload;
    },
    setBreeds(state, payload) {
      state.breeds = payload;
    }
  },
  getters: {
    dog: state => state.dog,
    breeds: state => state.breeds
  },
  actions: {
    async getBreeds({ commit }) {
      const response = await fetch("https://dog.ceo/api/breeds/list/all");
      const breeds = await response.json();
      commit("setBreeds", breeds);
    },
    async getDog({ commit }) {
      const response = await fetch("https://dog.ceo/api/breeds/image/random");
      const dog = await response.json();
      commit("setDog", dog);
    },
    async getBreedsAndDog({ dispatch }) {
      await dispatch("getBreeds");
      await dispatch("getDog");
    }
  }
});

new Vue({
  el: "#app",
  store,
  methods: {
    ...Vuex.mapActions(["getBreedsAndDog"])
  },
  computed: {
    ...Vuex.mapGetters(["breeds", "dog"])
  }
});
```

`index.html`:


```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vuex"></script>
  </head>
  <body>
    <div id="app">
      <button @click="getBreedsAndDog">Get Breeds and Dog</button>
      <p>{{Object.keys(breeds.message).slice(0, 3).join(',')}}</p>
      <img :src="dog.message" />
    </div>
    <script src="./index.js"></script>
  </body>
</html>
```

In the code above, we have the `getBreeds` and `getDog`, which are actions with one mutation commit each to the store.

Then we have `getBreedsAndDog` which is an action that dispatches the above two actions.

In `index.html`, we display all the states that are stored in the store.

We should retrieve the first 3 breed names and also the dog image that we got from the `getBreed` and `getDog` actions that were called by the `getBreedsAndDog` action.

The `getBreedsAndDog` action is mapped to the `getBreedsAndDog` method, so we can just call it to dispatch the action.

## Modules
We can divide our store into modules to segregate actions, mutations, and getters.

For example, we can write two modules and use them as follows:

`index.js`:

```javascript
const dogModule = {
  namespaced: true,
  state: {
    dog: {}
  },
  mutations: {
    setDog(state, payload) {
      state.dog = payload;
    }
  },
  getters: {
    dog: state => state.dog
  },
  actions: {
    async getDog({ commit }) {
      const response = await fetch("https://dog.ceo/api/breeds/image/random");
      const dog = await response.json();
      commit("setDog", dog);
    }
  }
};

const countModule = {
  namespaced: true,
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  getters: {
    count: state => state.count
  }
};

const store = new Vuex.Store({
  modules: {
    dogModule,
    countModule
  }
});

new Vue({
  el: "#app",
  store,
  methods: {
    ...Vuex.mapActions("dogModule", ["getDog"]),
    ...Vuex.mapMutations("countModule", ["increment"])
  },
  computed: {
    ...Vuex.mapState({
      count: state => state.countModule.count,
      dog: state => state.dogModule.dog
    })
  }
});
```

`index.html`:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vuex"></script>
  </head>
  <body>
    <div id="app">
      <button @click="getDog">Get Dog</button>
      <button @click="increment">Increment</button>
      <p>{{count}}</p>
      <img :src="dog.message" />
    </div>
    <script src="./index.js"></script>
  </body>
</html>
```

In the code above, we defined the `dogModule` and `countModule`, which have the state, mutations, getters, and actions as we have before.

Then, to create our store, we write:

```javascript
const store = new Vuex.Store({
  modules: {
    dogModule,
    countModule
  }
});
```

Then, when we map our actions and mutations, we have to specify the module as follows:

```javascript
methods: {
    ...Vuex.mapActions("dogModule", ["getDog"]),
    ...Vuex.mapMutations("countModule", ["increment"])
},
```

Then, when we map getters, we have to write functions to get them from the right module as follows:


```javascript
computed: {
    ...Vuex.mapState({
      count: state => state.countModule.count,
      dog: state => state.dogModule.dog
    })
}
```

## Conclusion
We can store shared state in Vue apps with Vuex.

It has state to store the states, getters to get data from states, mutations to change data, and actions to run one or more mutations or other actions and also run them asynchronously.

We can map states into computed properties with `mapGetters` and also map mutations and actions with `mapMutations` and `mapActions` methods respectively.

Finally, we can divide our store into modules to segregate them into smaller pieces.

---

**Before deploying your commercial or enterprise Vue apps to production, make sure you are protecting their code against reverse-engineering, abuse, and tampering by following <a href="https://blog.jscrambler.com/how-to-protect-your-vue-js-application-with-jscrambler/?utm_source=dev.to&utm_medium=referral">this tutorial</a>**.