Implementing a basic eCommerce cart with and without Vuex

A step by step guide revealing the complexities that arise without state management.

Overview

Resources and Setup

Vue.js Devtools Chrome Extension

Github Repo

Mockapi.io

Axios

Image for post
Image for post
import Axios from 'axios'const axios = Axios.create({
baseUrl: 'https://YOUR-ENDPOINT-ID.mockapi.io'
});
export default axios

Run the project

npm run serve
Image for post
Image for post

Part 1: Without state management.

Image for post
Image for post
<template>
<router-link :to="{name: 'Cart'}" class="button is-primary">
Cart ({{count}})
</router-link>
</template>

<script>
export default {
props: {
count: Number
}
}
</script>
<cart-btn :count="cartLength" class="level-right"></cart-btn>
<script>
import cartBtn from '@/components/CartButton.vue'
import axios from '@/axios'

export default {
data () {
return {
cart: []
}
},
computed: {
cartLength () {
return this.cart.length
}
},
components: {
'cart-btn': cartBtn
},
methods: {
async getCart() {
await axios.get(`cart`).then(response => {
this.cart = response.data
})
}
},
created() {
this.getCart()
}
}
</script>
Image for post
Image for post
addToCart () {
axios.post('cart', this.product).then(() => {
this.$emit('add-product-to-cart')
})

},
<add-to-cart :product="product" v-on:add-product-to-cart="$emit('add-product-to-cart')"></add-to-cart>
Image for post
Image for post
<add-to-cart :product="product" v-on:add-product-to-cart="$emit('add-product-to-cart', product)"></add-to-cart>
Image for post
Image for post
<router-view v-on:add-product-to-cart="onAddProductToCart"/>
onAddProductToCart (product) {
this.cart.unshift(product)
}
Image for post
Image for post

It works!

Image for post
Image for post

Part 2: Setting up State Management with Vuex

npm install vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);

export default new Vuex.Store({
state: {},
getters: {},
actions: {},
mutations: {}
})
import Vue from 'vue'
import App from './App.vue'
import store from '@/store'
import router from './router'

Vue.config.productionTip = false

new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')

Vuex Store Anatomy Basic Overview

Setting up state and getters

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);

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

export default new Vuex.Store({
state: {
cart: []
},
getters: {
getCart: state => state.cart
},
actions: {},
mutations: {}
})
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);

export default new Vuex.Store({
state: {
cart: []
},
getters: {
getCart: state => state.cart,
getCartLength: (state, getters) => {
return getters.getCart.length
}
},
actions: {},
mutations: {}
})
<template>
<router-link :to="{name: 'Cart'}" class="button is-primary">
Cart ({{count}})
</router-link>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
computed: {
...mapGetters({
count: 'getCartLength'
})
}

}
</script>

Setting up actions and mutations

async getCart() {
await axios.get(`cart`).then(response => {
this.cart = response.data
})
},
<script>
import cartBtn from '@/components/CartButton.vue'

export default {
components: {
'cart-btn': cartBtn
},
methods: {
onAddProductToCart (product) {
this.cart.unshift(product)
}
}
}
</script>
import Vue from 'vue'
import Vuex from 'vuex'
import axios from '@/axios'
Vue.use(Vuex);

export default new Vuex.Store({
state: {
cart: []
},
getters: {
getCart: state => state.cart,
getCartLength: state => state.cart.length
},
actions: {
async fetchCart() {
await axios.get(`cart`)
},

},
mutations: {
setCart: (state, cart) => (state.cart = cart)
}
})
export default new Vuex.Store({
state: {
cart: []
},
getters: {
getCart: state => state.cart,
getCartLength: state => state.cart.length
},
actions: {
async fetchCart({ commit }) {
await axios.get(`cart`).then(response => {
commit('setCart', response.data)
})

},
},
mutations: {
setCart: (state, cart) => (state.cart = cart)
}
})
<template>
<router-link :to="{name: 'Cart'}" class="button is-primary">
Cart ({{count}})
</router-link>
</template>

<script>
import { mapGetters, mapActions } from 'vuex'

export default {
computed: {
...mapGetters({
count: 'getCartLength'
})
},
methods: {
...mapActions({
fetchCart: 'fetchCart'
})
},
created () {
this.fetchCart()
}

}
</script>

Adding a product to state.cart

export default new Vuex.Store({
state: {
cart: []
},
getters: {
getCart: state => state.cart,
getCartLength: state => state.cart.length
},
actions: {
async fetchCart({ commit }) {
await axios.get(`cart`).then(response => {
commit('setCart', response.data)
})
},
async addToCart ({ commit }, product) {
axios.post('cart', product).then(() => {
commit('addProductToCart', product)
})
},

},
mutations: {
setCart: (state, cart) => (state.cart = cart),
addProductToCart: (state, product) => (state.cart.unshift(product))
}
})
<template>
<button @click="addToCart(product)" class="button is-primary">Add to Cart</button>
</template>

<script>
import { mapActions } from 'vuex'

export default {
props: {
product: {
type: Object,
required: true
}
},
methods: {
...mapActions({
addToCart: 'addToCart'
})

a̶d̶d̶T̶o̶C̶a̶r̶t̶ ̶(̶)̶ ̶{̶
̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶a̶x̶i̶o̶s̶.̶p̶o̶s̶t̶(̶'̶c̶a̶r̶t̶'̶,̶ ̶t̶h̶i̶s̶.̶p̶r̶o̶d̶u̶c̶t̶)̶.̶t̶h̶e̶n̶(̶(̶)̶ ̶=̶>̶ ̶{̶
̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶t̶h̶i̶s̶.̶$̶e̶m̶i̶t̶(̶'̶a̶d̶d̶-̶p̶r̶o̶d̶u̶c̶t̶-̶t̶o̶-̶c̶a̶r̶t̶'̶)̶
̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶}̶)̶
̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶}̶
}
}
</script>
<add-to-cart :product="product" ̶v̶-̶o̶n̶:̶a̶d̶d̶-̶p̶r̶o̶d̶u̶c̶t̶-̶t̶o̶-̶c̶a̶r̶t̶=̶"̶$̶e̶m̶i̶t̶(̶'̶a̶d̶d̶-̶p̶r̶o̶d̶u̶c̶t̶-̶t̶o̶-̶c̶a̶r̶t̶'̶,̶ ̶p̶r̶o̶d̶u̶c̶t̶)̶"̶></add-to-cart><router-view  ̶v̶-̶o̶n̶:̶a̶d̶d̶-̶p̶r̶o̶d̶u̶c̶t̶-̶t̶o̶-̶c̶a̶r̶t̶=̶"̶o̶n̶A̶d̶d̶P̶r̶o̶d̶u̶c̶t̶T̶o̶C̶a̶r̶t̶"̶/>

Implementing our getters and actions on the Cart.vue component

<script>
import { mapGetters, mapActions } from 'vuex'
import removeFromCart from '@/components/RemoveFromCart'

export default {
computed: {
...mapGetters({
cart: 'getCart'
})
},

components: {
'remove-from-cart': removeFromCart
},
methods: {
...mapActions({
fetchCart: 'fetchCart'
}),

onRemoveFromCart (productId) {
this.cart = this.cart.filter(product => product.id !== productId)
}
},
created () {
this.fetchCart()
}

}
</script>

Conclusion

Frontend Dev at Neutron Interactive

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store