Adding Modules to a Vuex Store

John Au-Yeung
Apr 25, 2020 · 5 min read
Photo by Clark Street Mercantile on Unsplash

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

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.

Photo by Mike Petrucci on Unsplash

Register Global Action in Namespaced Modules

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

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

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.

The Startup

Get smarter at building your thing. Join The Startup’s +787K followers.

Sign up for Top 10 Stories

By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

John Au-Yeung

Written by

Web developer. Subscribe to my email list now at https://thewebdev.info/subscribe/. Email me at hohanga@gmail.com

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +787K followers.

John Au-Yeung

Written by

Web developer. Subscribe to my email list now at https://thewebdev.info/subscribe/. Email me at hohanga@gmail.com

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +787K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

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