Adding Modules to a Vuex Store
Tags: vue, webdev, programming, javascript
Published: 2021-01-25 23:36:00

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

With Vuex, we can store our Vue app’s state in a central location.

In this article, we’ll look at how to add modules to separate a Vuex store into smaller parts.

Dividing a Store into Modules
=============================

Vuex uses a single state tree. This means the states are located in one big object. This will be bloated is our app grows big.

To make a Vuex store easier to scale, it can be separated into modules. Each module can have its own state, mutations, getters, and actions.

The `state` parameter in mutations and getters are the module’s local state.

By default, all actions, mutations, and getters inside modules are registered under a global namespace. This allows multiple modules to react to the same mutation or action type.

We can divide our store into module as in the following example:

`index.js` :

```
const moduleA = {  
  state: {  
    count: 0  
  },  
  mutations: {  
    increase(state, payload) {  
      state.count += payload.amount;  
    }  
  },  
  actions: {  
    increase({ commit }, payload) {  
      commit("increase", payload);  
    }  
  }  
};

const moduleB = {  
  state: {  
    count: 1  
  },  
  mutations: {  
    increase(state, payload) {  
      state.count += payload.amount;  
    }  
  },  
  actions: {  
    increase({ commit }, payload) {  
      commit("increase", payload);  
    }  
  }  
};

const store = new Vuex.Store({  
  modules: {  
    a: moduleA,  
    b: moduleB  
  }  
});

console.log(store.state.a.count);  
console.log(store.state.b.count);
```

Then in the `console.log` output, we should see 0 and 1 since `moduleA` ‘s initial `count` state is 0 and `moduleB` ‘s initial `count` state is 1.

To make each module self-contained, we have to namespace it by setting the `namespaced` option to `true` .

We can namespace a module and then call `dispatch` on actions after namespacing the modules as follows:

`index.js` :

```
const moduleA = {  
  namespaced: true,  
  state: {  
    count: 0  
  },  
  mutations: {  
    increase(state, payload) {  
      state.count += payload.amount;  
    }  
  },  
  actions: {  
    increase({ commit }, payload) {  
      commit("increase", payload);  
    }  
  }  
};

const moduleB = {  
  namespaced: true,  
  state: {  
    count: 1  
  },  
  mutations: {  
    increase(state, payload) {  
      state.count += payload.amount;  
    }  
  },  
  actions: {  
    increase({ commit }, payload) {  
      commit("increase", payload);  
    }  
  }  
};

const store = new Vuex.Store({  
  modules: {  
    a: moduleA,  
    b: moduleB  
  }  
});

new Vue({  
  el: "#app",  
  store,  
  computed: {  
    ...Vuex.mapState({  
      a: state => state.a,  
      b: state => state.b  
    })  
  },  
  methods: {  
    increaseA(payload) {  
      this.$store.dispatch("a/increase", payload);  
    },  
    increaseB(payload) {  
      this.$store.dispatch("b/increase", payload);  
    }  
  }  
});
```

`index.html` :

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vuex"></script>  
  </head>  
  <body>  
    <div id="app">  
      <button @click="increaseA({amount: 10})">Increase</button>  
      <button @click="increaseB({amount: 10})">Increase</button>  
      <p>A Count: {{a.count}}</p>  
      <p>B Count: {{b.count}}</p>  
    </div>  
    <script src="index.js"></script>  
  </body>  
</html>
```

In the code above, we have `namespaced: true` set in each module, and then we added 2 methods to our Vue instance to dispatch the namespaced actions:

```
increaseA(payload) {  
  this.$store.dispatch("a/increase", payload);  
}
```

and:

```
increaseB(payload) {  
  this.$store.dispatch("b/increase", payload);  
}
```

Since we have `namespaced` set to `true` , we have to dispatch the actions by passing in `“a/increase”` and `“b/increase”` to `dispatch` .

Then once we clicked the buttons, our methods are called to dispatch the actions and the numbers will increase.

Register Global Action in Namespaced Modules
============================================

We can also register global actions in namespaced modules, by setting the `root` option to `true` and place the action definition in the function handler.

To do register a global action, we can write something like the following code:

`index.js` :

```
const moduleA = {  
  namespaced: true,  
  state: {  
    count: 0  
  },  
  mutations: {  
    increase(state, payload) {  
      state.count += payload.amount;  
    }  
  },  
  actions: {  
    increase({ commit }, payload) {  
      commit("increase", payload);  
    }  
  }  
};

const moduleB = {  
  namespaced: true,  
  state: {  
    count: 1  
  },  
  mutations: {  
    increase(state, payload) {  
      state.count += payload.amount;  
    }  
  },  
  actions: {  
    increase({ commit }, payload) {  
      commit("increase", payload);  
    }  
  }  
};

const rootModule = {  
  actions: {  
    increaseAll: {  
      root: true,  
      handler(namespacedContext, payload) {  
        namespacedContext.commit("a/increase", payload);  
        namespacedContext.commit("b/increase", payload);  
      }  
    }  
  }  
};

const store = new Vuex.Store({  
  modules: {  
    a: moduleA,  
    b: moduleB,  
    root: rootModule  
  }  
});

new Vue({  
  el: "#app",  
  store,  
  computed: {  
    ...Vuex.mapState({  
      a: state => state.a,  
      b: state => state.b  
    })  
  },  
  methods: {  
    ...Vuex.mapActions(["increaseAll"])  
  }  
});
```

`index.html` :

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vuex"></script>  
  </head>  
  <body>  
    <div id="app">  
      <button @click="increaseAll({amount: 10})">Increase All</button>  
      <p>A Count: {{a.count}}</p>  
      <p>B Count: {{b.count}}</p>  
    </div>  
    <script src="index.js"></script>  
  </body>  
</html>
```

In the code above, we added the `rootModule` which has the global action as follows:

```
const rootModule = {  
  actions: {  
    increaseAll: {  
      root: true,  
      handler(namespacedContext, payload) {  
        namespacedContext.commit("a/increase", payload);  
        namespacedContext.commit("b/increase", payload);  
      }  
    }  
  }  
};
```

A global action has the `root` option set to `true` and a `handler` method which is used to dispatch actions from any module.

Then in the Vue instance, we have:

```
methods: {  
  ...Vuex.mapActions(["increaseAll"])  
}
```

to map the `increaseAll` action to a method in the Vue instance.

Then in the template, we have:

```
<button @click="increaseAll({amount: 10})">Increase All</button>
```

to call the `increaseAll` method returned from the `mapActions` method when the button is clicked.

Then we should both numbers increasing since we mapped both module’s state to the Vue instance’s data.

Dynamic Module Registration
===========================

We can also register a module dynamically by using the `store.registerModule` method as follows:

`index.js` :

```
const moduleA = {  
  state: {  
    count: 0  
  },  
  mutations: {  
    increase(state, payload) {  
      state.count += payload.amount;  
    }  
  },  
  actions: {  
    increase({ commit }, payload) {  
      commit("increase", payload);  
    }  
  }  
};

const store = new Vuex.Store({});

store.registerModule("a", moduleA);

new Vue({  
  el: "#app",  
  store,  
  computed: {  
    ...Vuex.mapState({  
      a: state => state.a  
    })  
  },  
  methods: {  
    ...Vuex.mapActions(["increase"])  
  }  
});
```

`index.html` :

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vuex"></script>  
  </head>  
  <body>  
    <div id="app">  
      <button @click="increase({amount: 10})">Increase</button>  
      <p>A Count: {{a.count}}</p>  
    </div>  
    <script src="index.js"></script>  
  </body>  
</html>
```

The `store.registerModule` takes a string for the module name, and then an object for the module itself.

Then we can call the helpers to map getters to computed properties and actions/mutations to methods. In the code above, we have the `increase` action mapped to a method.

Then we can call it in our template as usual.

Conclusion
==========

If our Vuex store is big, we can divide it into modules.

We can register modules when we create the store or dynamically with `registerModule` .

Then we can map actions/mutations by their name as usual, and we can map the state by accessing `state.a.count` , where `a` is the module name, and `count` is the state name. Replace it with our own module and state names if the code is different.

We can also namespace the modules. Then we dispatch the actions starting with the module name and a slash instead of just the name.