SPA State Management - Vuex

Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.

Vuex helps us deal with shared state management with the cost of more concepts and boilerplate. It's a trade-off between short term and long term productivity.

Vuex Stores are reactive. Each change in the store will update the components that use that part of the data.

  • The state, the source of truth that drives our app;

  • The view, a declarative mapping of the state;

  • The actions, the possible ways the state could change in reaction to user inputs from the view.

Mutations

The only way to actually change the State in a Vuex store is by committing a mutation. Vuex mutations are very similar to events: each mutation has a string type and a handler. The handler function is where we perform actual state modifications, and it will receive the state as the first argument.

Actions

Actions are similar to mutations, the differences being that:

  • Instead of mutating the state, actions commit mutations.

  • Actions can contain arbitrary asynchronous operations.

Getters

Vuex allows us to define "getters" in the store. You can think of them as computed properties for stores. 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.

export const store = new Vuex.Store({
  state: {
    counter: 0
  },
  //showing things, not mutating state
  getters: {
    tripleCounter: state => {
      return state.counter * 3
    }
  },
  //mutating the state
  //mutation are always synchronous
  mutations: {
    increment: (state, num) => {
      state.counter += num
    }
  },
  //commits the mutation, it's asynchronous
  actions: {
    //showing passed with payload (an object)
    asyncIncrement: ({ commit }, payload) => {
      setTimeout(() => {
        commit('increment', payload.by)
      }, payload.duration)
    }
  }
})

Why use Vuex?

  • Simplified data flow: Makes it easier to manage data in complex applications, especially when you have many components sharing and modifying the same data.

  • Predictable state changes: Vuex enforces rules (mutations) to ensure that state changes happen in a predictable and trackable way, making debugging easier.

  • Reactivity: Vuex stores are reactive, so when the state changes, components that use that data automatically update.

  • Improved organization: Helps keep your data logic separate from your component logic, making your code cleaner and more maintainable.

Usage in components

State:

this.$store.state.nameOfThing

Getters:

this.$store.getters.nameOfThing

Mutations:

this.$store.commit('nameOfThing', payload)

Actions:

this.$store.dispatch('nameOfThing', payload)

//...
computed: {
  count() { //this is cached until the dependecy is changed
    return this.$store.state.count
  },
  tripleCounter() {
    return this.$store.getters.tripleCounter
  }
},
methods: {
  increment() {
    this.$store.commit('increment', 2)
  },
  asyncIncrement() {
    this.$store.dispatch('asyncIncrement', 2) //2 is the payload
  }
}
//...

Mapping the State

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 mapState helper which generates computed getter functions for us.

import { mapState } from 'vuex'

export default {
  //...
  computed: {
    ...mapState([
      //map this.count to this.$store.state.count
      'count', //destructuring
      'posts',
      'items'
    ])
  }
}

This is similar for mapGetters, mapMutations, mapActions.

Full Docs

https://vuex.vuejs.org/

Last updated

Was this helpful?