Structuring store with the right feet using vue/vuex

Chris Michael
5 min readMay 23, 2019

--

This publication will be focused on how to structure our store in a simple and effective way. I will be assuming that you have a fairly reasonable knowledge using the vue framework and the handling of Lifecycle Hooks

What is Vuex?

Vuex is a state management pattern based on the singleton architecture that allows us to have centralized and accessible data in any component within the application but without being able to be modified without some control.

Why should we use Vuex?

By using a centralized application data store, the complete state of the application can be represented in one place, which makes the application more organized. By using a unidirectional data flow, mutations and access to the data of the scope component only to the required data, it becomes much simpler to reason about the role of the component and how it should affect the state of the application.

Store structure

store.js

It is better to keep all the Vuex logic in a separate file. We will keep our logic in a file called store.js. In our store.js file we have a store object that is our Vuex store.

The store is an instance of the Vuex.store object, which consists of four objects. The four objects are the state, actions, mutations and getters.

Since these objects have been defined in different files, they must be imported into the store.js file and then passed in the store instance.

import Vue from 'vue';
import Vuex from 'vuex';
import {state} from './state';
import {actions} from './actions';
import {mutations} from './mutations';
import {getters} from './getters';
Vue.use(Vuex);export default new Vuex.Store({
state,
mutations,
actions,
getters
});

state.js

The state is an object that contains the state of the application data.

export const state = {
data: [],
isLoading: true
};

mutations.js

Mutations is also an object that contains methods that affect the state and only care to control the states, to manipulate it.

A mutation may have two arguments as state and payload:

  • State has the current state of an application.
  • Payload is an optional one, which will give the data to mutate.
 export const mutations = {
SET_DATA(state , payload){
state.data = payload;
},
IS_LOADING(state , payload){
state.isLoading = payload;
}
};

actions.js

Actions are methods used to cause mutations and execute asynchronous code. Responsible for preparing everything necessary for a mutation to confirm a change in the state.

The actions expect a promise to be resolved, hence we make a return of the promise that returns axios. When axios returns the data, we can execute commits with the type of mutation that we want to carry out. Instead of mutating the state, actions commit mutations, in this case, using the mutator SET_DATA. And the mutator IS_LOADING that will help to know if there is still data to load.

import axios from 'axios';const BASE_API_URL= '...';
const API_URL_ENDPOINT = '...';
const A = axios.create({ baseURL: String(BASE_API_URL) });export const actions = {
GET_DATA({commit}){
A.get(API_URL_ENDPOINT).then((res) =>{
commit('SET_DATA' , res.data);
commit('IS_LOADING' , false);
}).catch((err) =>{
console.error(err)
});
}
};

getters.js

Getters contain the methods used to abstract access to the state and to do some preprocessing tasks, if necessary (data calculation, filtering, etc …).

Vuex allows us to define “getters” in the store. Like computed properties, a getter’s result is cached based on its dependencies, and will only re-evaluate when some of its dependencies have changed.

  • Getters will receive the state as their 1st argument

For the following example, we will be using Lodash’s library. Lodash is a well-known JavaScript utility library that makes it easy to manipulate arrays and objects, as well as functions, strings, etc.

So, since we are interested in filtering some data in particular, we will use the function _.filter().

_.filter(collection, [predicate=_.identity])

Iterates over elements of collection, returning an array of all elements predicate returns truthy for. The predicate is invoked with three arguments: (value, index|key, collection).

var _ = require('lodash');export const getters = {
FILTER_SPESIFIC_DATA: (state) =>{
return _.filter(state.data , (data) =>{
// ....
});
}
};

Working in the view folder

Home.vue

  • When a component needs to make use of multiple store state properties or getters, declaring all these computed properties can get repetitive and verbose. To deal with this we can make use of the mapStatehelper which generates computed getter functions for us.
  • The easy way to access getters in your component, however, is through Vuex’s mapGetter helper method. This allows you to mount getters to top-level computed properties in your component.

Note that mapState returns an object. How do we use it in combination with other local computed properties? Normally, we'd have to use a utility to merge multiple objects into one so that we can pass the final object to computed. However with the object spread operator (which is a stage-4 ECMAScript proposal), we can greatly simplify the syntax, equally applies with mapGetters .

Spread Properties

Spread properties in object initializers copies own enumerable properties from a provided object onto the newly created object.

<template>
<div class="content">
<div v-if="isLoading"> loading...</div>
<div v-else>
<div v-for="(content , index) in data" :key="index">
// ....
</div>
</div>
</div>
</template>
<script>
import {mapState , mapGetters} from 'vuex';
import store from '../store/store.js';

export default{
name: 'home',
computed:{
...mapState(['data' , 'isLoading']),
...mapGetters(['FILTER_SPESIFIC_DATA']),
filteredDataObj(){
return FILTER_SPESIFIC_DATA()
}
},
created(){
store.dispatch('GET_DATA')
}
};
</script>
<style scoped>
.content{
....
}
</style>

Finally…..

main.js

Now, to access the store you’ll either have to import it in all of your components, or you can inject it into the root Vue instance to have it automatically injected into every other component in your app asthis.$store or importing the store.js file from the store folder.

import Vue from 'vue';
import Vuex from 'vuex';
import App from 'App.vue';
import { store } from '../store/store.js';
Vue.config.productionTip = false;Vue.use(Vuex);new Vue({
store,
render: h => h(App)
}).$mount('#app');

Conclusion

Keep in mind that vuex helps us maintain and test our application better, but we also have to be aware that vuex involves new levels of abstraction that will make us need to work with new concepts that will make the learning curve less accessible for developed juniors to our projects. So be careful.

Github Starter Template

Vue Store Starter CLI

https://www.npmjs.com/package/vuex-store-starter-cli/v/1.1.0?fbclid=IwAR3X-QIcoa_2MdXcaVHOQRNjTaMxDr8Ch091UUxCA0ra9yas2N-gBucGhf4`

References

  • The Progressive JavaScript Framework. Retrieved May 22, 2019, from https://vuejs.org/

I’ll be glad that you like this article! 💖

--

--

Chris Michael
0 Followers

Former student of Computer Science. Interested in topics related to algorithms, especially in Graph Theory. A very nice person to meet and talk.