You might be tempted to update state from your component this way:

this.products.push({})

Vue needs to keep track of these changes when they are made. Therefore, the above is not allowed. Changes are made to the state using a store member, Mutations.

When a change needs to be made, we commit to mutations, and the mutation function makes this update. Mutations must by synchronous, Actions are for asynchronous tasks.

Table of Contents

    export const productMutations = {
      addProduct (state, payload) {
        state.showLoader = true
        state.products.push(payload)
      }
    }
    //
    //
    // store
    mutations: Object.assign({}, productMutations)
    //
    //
    // commit
    methods: {
      addProduct(product) {
        this.$store.commit('addProduct', product)
      }
    }

    The commit method takes the name of the mutation handler and the payload. The handler addProduct receives the current state and payload and appends the payload to the state's products.

    Constants as Mutation Types

    addProduct must be used in two or more places. When creating the mutation and while committing to the mutation. This creates a layer of possible typographical error. You can use constants to replace these types and allow tools like IntelliSense to help you avoid typos and errors:

    // ./src/store/mutation-types
    export const ALL_PRODUCTS = 'ALL_PRODUCTS'
    export const ALL_PRODUCTS_SUCCESS = 'ALL_PRODUCTS_SUCCESS'
    
    export const PRODUCT_BY_ID = 'PRODUCT_BY_ID'
    export const PRODUCT_BY_ID_SUCCESS = 'PRODUCT_BY_ID_SUCCESS'
    
    export const ADD_PRODUCT = 'ADD_PRODUCT'
    export const ADD_PRODUCT_SUCCESS = 'ADD_PRODUCT_SUCCESS'
    
    export const UPDATE_PRODUCT = 'UPDATE_PRODUCT'
    export const UPDATE_PRODUCT_SUCCESS = 'UPDATE_PRODUCT_SUCCESS'
    
    export const REMOVE_PRODUCT = 'REMOVE_PRODUCT'
    export const REMOVE_PRODUCT_SUCCESS = 'REMOVE_PRODUCT_SUCCESS'
    
    export const ADD_TO_CART = 'ADD_TO_CART'
    export const REMOVE_FROM_CART = 'REMOVE_FROM_CART'
    
    export const ALL_MANUFACTURERS = 'ALL_MANUFACTURER'
    export const ALL_MANUFACTURERS_SUCCESS = 'ALL_MANUFACTURER_SUCCESS'
    

    Just a string mapped to a constant. Nothing more.

    We can now use these constants both for creating mutations and committing to them.

    First import the constants into our mutations file:

    // ./src/store/mutations
    
    import {
      ADD_PRODUCT,
      ADD_PRODUCT_SUCCESS,
      PRODUCT_BY_ID,
      PRODUCT_BY_ID_SUCCESS,
      UPDATE_PRODUCT,
      UPDATE_PRODUCT_SUCCESS,
      REMOVE_PRODUCT,
      REMOVE_PRODUCT_SUCCESS,
      ADD_TO_CART,
      REMOVE_FROM_CART,
      ALL_PRODUCTS,
      ALL_PRODUCTS_SUCCESS,
      ALL_MANUFACTURERS,
      ALL_MANUFACTURERS_SUCCESS
    } from './mutation-types'

    Now we have access to the them and can use them as mutation function names. The most important mutation is the product mutation which mutates the product state. Here are the mutation methods:

    // ./src/store/mutations
    
    // Constant imports ...
    
    export const productMutations = {
      [ALL_PRODUCTS] (state) {
        // Called when fetching products
        state.showLoader = true
      },
      [ALL_PRODUCTS_SUCCESS] (state, payload) {
        // Called when products have been fetched
        state.showLoader = false
        // Updates state products
        state.products = payload
      },
      [PRODUCT_BY_ID] (state) {
        // Called when fetching products by ID
        state.showLoader = true
      },
      [PRODUCT_BY_ID_SUCCESS] (state, payload) {
        // Called when product is fetched
        state.showLoader = false
        // Updates state product
        state.product = payload
      },
      [ADD_PRODUCT]: (state, payload) => {
        // ...Same pattern
        state.showLoader = true
      },
      [ADD_PRODUCT_SUCCESS]: (state, payload) => {
        state.showLoader = false
        state.products.push(payload)
      },
      [UPDATE_PRODUCT]: (state, payload) => {
        state.showLoader = true
      },
      [UPDATE_PRODUCT_SUCCESS]: (state, payload) => {
        state.showLoader = false
        state.products = state.products.map(p => {
          if (p._id === payload._id) {
            payload = {...payload, manufacturer: state.manufacturers.filter(x => x._id === payload.manufacturer)[0]}
            return payload
          }
          return p
        })
      },
      [REMOVE_PRODUCT]: (state, payload) => {
        state.showLoader = true
      },
      [REMOVE_PRODUCT_SUCCESS]: (state, payload) => {
        state.showLoader = false
        const index = state.products.findIndex(p => p._id === payload)
        console.debug('index', index)
        state.products.splice(index, 1)
      }
    }

    Each of the mutation has a SUCCESS variation which is called after mutation is completed. The actual mutations just kicks of a pending state by showing the loading spinner. Then the SUCCESS mutations update the UI.

    Next, we do the same thing for the cart and manufacturer:

    // ./src/store/mutations
    export const cartMutations = {
      [ADD_TO_CART]: (state, payload) => state.cart.push(payload),
      [REMOVE_FROM_CART]: (state, payload) => {
        const index = state.cart.findIndex(p => p._id === payload)
        state.cart.splice(index, 1)
        console.log(state.cart, state.cart.length, index)
      }
    }
    
    export const manufacturerMutations = {
      [ALL_MANUFACTURERS] (state) {
        state.showLoader = true
      },
      [ALL_MANUFACTURERS_SUCCESS] (state, payload) {
        state.showLoader = false
        state.manufacturers = payload
      }
    }
    

    The mutations are added to the store afterwards:

    mutations: Object.assign({}, productMutations, cartMutations, manufacturerMutations),

    Next, we take a look at how actions trigger these mutations for for async operations.

    Chris Nwamba

    104 posts

    JavaScript Preacher. Building the web with the JS community.