When an app becomes complex in terms of where data is being passed between multiple components, it is time to make use of Vuex. Vuex is a state management pattern + library for Vue.js applications. It serves as a centralised store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.
Why use Vuex?
- Vuex provides one source of truth
- Every component has access to the global state
- The global state is reactive, just like local state
- Reduces component data tracking from becoming complex.
- Vuex provides a state pattern to make updates standardized.
To set up Vuex you have to install the npm package.
npm i vuex --save
Inside the src folder, we can now set up a store.js file.
Heres how the store.js file would look.
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {},
mutations: {},
actions: {},
getters: {},
});
Inside main.js we can link the store to the Vue instance by importing the store file and added to Vueโฆ
import Vue from 'vue';
import App from './App.vue';
import { store } from './store/store';
Vue.config.productionTip = false;
new Vue({
store,
render: h => h(App),
}).$mount('#app');
Right now we have the store set up ๐ฅณ but we cannot access or edit yet ๐.
Mutations
- Commit + track state changes
- The best practice is for actions to call mutations then mutations update the state directly.
- Using the dev tools we can roll back mutations back to the previous state value.
Getting the state from Vuex into computed properties.
Inside the store.js we can create getters, these interact directly with the state in the store. They are used to get the state. We can then display the getters inside components through computed properties.
Documentation for getters here.
Store.js file with state and getters set up.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
user: { id: 'aaa', name: 'Adsasas' },
events: [
{
id: 1,
title: 'title one',
organizer: 'a'
},
{
id: 2,
title: 'title two',
organizer: 'b'
},
{
id: 3,
title: 'title three',
organizer: 'c'
}
],
categories: [
'sustainability',
'nature',
'animal welfare',
'housing',
'education',
'food',
'community'
]
},
mutations: {},
actions: {},
getters: {
catLength: state => {
return state.categories.length
},
doneToDos: state => {
return state.todos.filter(todo => todo.done)
},
activeTodosCount: state => {
return state.todos.filter(todo => !todo.done).length
},
getEventById: state => id => {
return state.events.find(event => event.id === id)
}
}
})
How to link the getters to components?
Use computed properties, but first, we can map the getters for easy access like so.
<template>
<div>
<h1>Create Event {{ userName }}</h1>
<p>There are {{catLength}} categories</p>
<ul>
<li v-for="cat in categories" :key="cat">
{{cat}}
</li>
</ul>
<p>{{ getEventById(3) }}</p>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex';
export default {
computed: {
...mapState(['user', 'categories']),
...mapGetters(['getEventById', 'catLength'])
}
}
</script>
You have to import mapGetters from Vuex to be able to place inside the computed properties. Using the spread operator and pacing each getter as a string array item we can then reference individual getters inside the template.
We can also create getters that take arguments which are then used as a computed property and take input inside the template. The example for this is the getter getEventById in the above two code blocks.
Mutations
- Can update/mutate Vuex State.
- They are synchronous, they happen one after another.
Mutations documentation here..
Actions
- Are asynchronous, they may not happen in the order they appear in the code.
- Can wrap business logic around mutations
- Always put Mutations within Actions โ as business logic, later on, would be easy to apply, this increases the scalability.
Here we can see that mutation has been created that takes an event and pushes to the events array. The action is the code that we will dispatch to from the component, in turn, this calls the mutation and updates the store state.
Let's see how to call the action inside a component.
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
categories: ['travel', 'fun', 'social', 'work', 'other'],
events: [],
},
getters: {
cat: state => {
return state.categories;
},
getEventById: state => id => {
return state.events.find(event => event.id === id);
},
events: state => {
return state.events;
},
},
mutations: {
ADD_EVENT(state, event) {
state.events.push(event);
},
},
actions: {
createEvent({ commit }, event) {
commit('ADD_EVENT', event);
},
},
});
this.$store.dispatch('createEvent', this.event)
Referencing the store and dispatch with the name that matches actions function name, this will now send the this. event data into the action, then call the mutation and update the state.
Using the pattern above is a way to set up and use Vuex as the data point in your application was helps avoid tracking through components to see where the data needs to pass to. This can become bloated in larger applications so a module approach is needed to aid larger apps. Modules documentation can be found here.
Top comments (0)