Part 2: Understanding Vuex: A State Management Library

Part 2: Understanding Vuex: A State Management Library

·

6 min read

What is Vuex?

In modern web applications, managing the state could become messy when the application grows in size and complexity. This is where Vuex, a state management library for Vue.js, comes in. Think of the Vuex store as a big container that holds all the important information needed by your components. Instead of passing data back and forth between components, you can simply put it in the store and access it from anywhere in your application. It provides a centralized approach for managing data used in all components of an application.

Note: The choice of using Vuex in this series is because I'm using Vue 2. If by chance you want to learn something about Pinia, which is the new state management in VueJs, kindly check here.

Vuex follows a one-way flow pattern, which helps in maintaining a clear and predictable data flow, making it easier to understand and debug the application's state changes.

In a one-way flow pattern, data originate from a source, in Vuex's case the "store". The store contains the application's state, and components can access and modify the state by committing mutations or dispatching actions.

An example code here src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

Here's a breakdown of the key components and concepts in Vuex:

1.state: The state represents the application's data. It's a single JavaScript object that serves as the source of truth for the entire application. All components can access the state but cannot directly modify it.

2.getters: Getters are functions that allow you to derive and access specific pieces of state from the store. They provide a computed property-like interface to access the state and perform calculations or transformations on it.

3.mutations: Mutations in Vue help us update and modify data in a controlled way. They make it possible for our components to react and change based on different conditions or user interactions. It is like making a change or adjustment to something in order to make it different. For example, when you have a picture and you decide to draw or paint something new on it to change how it looks. For you to make changes to a vuex state, you have to commit a mutation either by dispatching an action or by invoking it directly from a component.

4.actions: Actions are similar to mutations, but they can handle asynchronous operations. Actions can commit mutations and perform tasks such as fetching data from an API, updating the state, and then committing the mutation to modify the state. Actions are invoked by dispatching an action.

5.modules: As an application grows, it can become challenging to manage a large centralized state. Modules in Vuex help break down the state and related logic into smaller, reusable, and manageable chunks. Each module can have its own state, getters, mutations, and actions.

Component Vuex communication

In this example, we will create a mini-app using the Vuex store as our central data storage medium. we will also utilize the module system to break the store into smaller bits just for this exercise. normally modules are supposed to come into play when the store size increases, but I will do a short example of it so you can get to see how it works moving forward.

This example is based on handling a form input to update the state in the store when a user dispatch an action from within a component. The updated data is then rendered on the components page.

  • Create state data, action and mutation

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    id: 2,
    name:'Component vue communication',
    stack:'VueJs',
  },
  getters: {
    getUserName(state){
      return state.name;
    }
  },
  mutations: {
    SET_USERNAME(state, payload){
      state.name = payload
    }
  },
  actions: {
    actionSetUsername({ commit }, data){
      commit('SET_USERNAME' , data);
    }
  },
  modules: {
  }
})
  • In MyComponent, an action "actionSetUsername" will be dispatched when the button Set Name is clicked

  • you will also have to map the username value from the store to display the current value

src/store/MyComponent.vue

<template>
   <div>
      <h1>{{ name }}</h1>
      <p>{{ email }}</p>
      <p>{{ myProp }}</p>
      <button @click="sendMessage">Send Message</button>

      <div class="container">
         <form @submit.prevent="submit">
            <input 
               v-model.trim="storeName" 
               type="text" 
               class="form__input btn_common"
            >
            <button class="container__btn btn_common">Set Name</button>
         </form>

         <!-- result of click action from store -->
         <h2>{{ username }}</h2>
      </div>
   </div>
</template>

<script>

import { mapState, mapActions } from 'vuex'

export default {
  name: 'MyComponent',
  props: {
      myProp: String
  },
  data() {
      return {
         storeName: '',
         name: 'Makanju Emmanuel',
         email: 'makurseme@gmail.com'
      }
  },
  methods: {
   ...mapActions(['actionSetUsername']),
   sendMessage() {
      this.$emit('message-sent', 'Hello from child!')
   },
   submit(){
      if(this.storeName !== ''){
        this.actionSetUsername(this.storeName)
      } 
   }
  },
  computed: {
     ...mapState({
        username: state => state.name,
     })
  }
}
</script>

<style lang="scss" scoped>
/* styles g*/
.container {
  width: 40%;
  margin: 1rem auto; 
  box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.3);
  border-radius: 5px;
  padding: 10px 5px;  

  & .btn_common {
     border: .75px solid #7373ed;
     padding: 5px 10px;
     border-radius: 5px;
  }

  & .form__input {
     outline: none;
     margin-right: 5px;
  }
}
</style>

Snapshot of the end result

Image description

How to create a vuex module

You can create as many modules to break store data into small and manageable chunks when things get bloated. for example: In a school portal you can create a user module that deals with authentication, you can also create another module for managing student records and so on.

  • Create a new module folder in your store directory

  • Create a module file: Create a separate module file for your Vuex module inside the new folder. This file will contain the state, mutations, actions, and getters specific to your module.


const myModule = {
   namespaced: true,
   state: {
     // Your module-specific state
     count: 0,
   },
   mutations: {
     // Your module-specific mutations
     INCREMENT(state){
        state.count  += 1;
     }
   },
   actions: {
      // Your module-specific actions
      incrementCount({commit}){
        commit('INCREMENT');
      },
   },
 };

 export default myModule;
  • Register the module in the store: Import your module file into your Vuex store and register it as a module using the modules property. The module will be namespaced under the specified namespace.
import Vue from 'vue'
import Vuex from 'vuex'
import myModule from '../store/modules/myModule'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    id: 2,
    name:'Component vue communication',
    stack:'VueJs',
  },
  getters: {
    getUserName(state){
      return state.name;
    }
  },
  mutations: {
    SET_USERNAME(state, payload){
      state.name = payload
    }
  },
  actions: {
    actionSetUsername({ commit }, data){
      commit('SET_USERNAME' , data);
    }
  },
  modules: {
    mod: myModule //register your module here
  }
})
  • Access the module in your components: You can now access the state, mutations, actions, and getters of your module in your Vue components using the $store object. To access module-specific elements, you need to prefix them with the module's namespace.

src/store/MyComponent.vue

<template>
   <div>
      <h1>{{ name }}</h1>
      <p>{{ email }}</p>
      <p>{{ myProp }}</p>
      <button @click="sendMessage">Send Message</button>

      <div class="container">
         <form @submit.prevent="submit">
            <input 
               v-model.trim="storeName" 
               type="text" 
               class="form__input btn_common"
            >
            <button class="container__btn btn_common">Set Name</button>
         </form>

         <!-- result of click action from store -->
         <h2>{{ username }}</h2>
         <h2>module count: {{ count }}</h2>
         <button type="submit" @click="myModuleAction">increment count</button>
      </div>
   </div>
</template>

<script>

import { mapState, mapActions } from 'vuex'

export default {
  name: 'MyComponent',
  props: {
      myProp: String
  },
  data() {
      return {
         storeName: '',
         name: 'Makanju Emmanuel',
         email: 'makurseme@gmail.com'
      }
  },
  methods: {
   ...mapActions(['actionSetUsername']),
   ...mapActions('mod' , ['incrementCount']),
   sendMessage() {
      this.$emit('message-sent', 'Hello from child!')
   },
   submit(){
      if(this.storeName !== ''){
        this.actionSetUsername(this.storeName)
      } 
   },
   myModuleAction() {
      this.incrementCount(); // Dispatch module action
   }

  },
  computed: {
     ...mapState({
        username: state => state.name,
     }),
     count() {
        return this.$store.state.mod.count; // Access module state
     }
  }
}
</script>

In conclusion, using Vuex modules in Vue.js applications provides a structured and scalable approach to managing state. Modules allow you to divide your store into smaller, self-contained units, making it easier to organize and maintain your application's state management.