Summary of Vuex Tutorial by Net Ninja

JL’s Self-teaching Story #5

Lim
Lim
Sep 4, 2018 · 10 min read
Copyright © 2018 Vue.js

[JL’s Self-teaching Story] Series

[Net Ninja] JS Regular Expressions
[Net Ninja] Vue JS2 (Part1, Part2, Part3)
[Net Ninja] Vuex(current)
[Net Ninja] Python3 (Part1, Part2, Part3)
[Net Ninja] Django (Part1, Part2, Part3)
[Net Ninja] Sass (Part1, Part2)
[Sean Larkin] Webpack4 (Part1, Part2, Part3, Part4)

🌲 This is my summary of Vuex Tutorial by Net Ninja on YouTube.


1) What is Vuex?
2) Simple Vue App - Vanlia VueJS
3) Setting up a Central Store
4) Using Computed Properties
5) Getters
6) Mutations
7) Actions
8) Mapping Actions & Getters

1) What is Vuex? (YouTube)

Vuex is a library & statement management pattern for use with Vue.js. It allows us to create a centralized data store, which can be accessed by all components in an app.

When should we use Vuex?

Although Vuex helps us deal with shared state management, it also comes with the cost of more concepts and boilerplate. It’s a trade-off between short term and long term productivity.

If you’ve never built a large-scale Single-Page Application(SPA) and jump right into Vuex, it may feel verbose and daunting. That’s perfectly normal — if your app is simple, you will most likely be fine without Vuex. A simple store pattern may be all you need.

But, if you are building a medium-to-large-scale SPA, chances are you have run into situations that make you think about how to better handle state outside of your Vue components, and Vuex will be the natural next step for you (Official website).

Let’s assume that we have a to-do list app.

Copyright © 2017 Net Ninja

If we create the to-do list app using only Vue.js, it is hard for children components to communicate with each other. (In this case, it would be “latest todos” and “add todo”).

Copyright © 2017 Net Ninja

The centralized data store contains shared data that can be directly accessed by any component in the app. The data doesn’t have to be passed down as a property through component chains.


Vue CLI is a standard tooling for Vue.js development (official website). It allows us to create a development environment workflow with webpack. Benefits of using webpack are the following:

In the video, Vue CLI2 was used. However, since the video had been released on April 30, 2017, Vue CLI3 came out. So, I used Vue CLI3 instead.

If you have the previous vue-cli (1.x or 2.x) package installed globally, you need to uninstall it first with npm uninstall vue-cli -g.

  1. Used npm install -g @vue/cli instead of npm install -g vue-cli .
  2. vue init webpack-simple <project-name> : install webpack-simple template
  3. To get started, the following needs to be entered in order: cd <project-name>, npm install(install all dependencies needed for webpack-simple), and npm run dev(run a development server).

2) Simple Vue App — Vanlia VueJS (YouTube)

In this chapter, a simple Vue App was created by using Vue.js only.

[src > App.vue]
<template>
<div id="app">
<product-list-one v-bind:products='products'></product-list-one>
<product-list-two v-bind:products='products'></product-list-two>
</div>
</template>

<script>
import ProductListOne from './components/ProductListOne.vue';
import ProductListTwo from './components/ProductListTwo.vue';

export default {
components: {
'product-list-one': ProductListOne,
'product-list-two': ProductListTwo,
},
name: 'app',
data () {
return {
products: [
{name: 'Banana Skin', price: 20},
{name: 'Shiny Star', price: 40},
{name: 'Green Shells', price: 60},
{name: 'Red Shells', price: 80}
]
}
}
}
</script>

<style>
body{
font-family: Ubuntu;
color: #555;
}
</style>
-----------------------------------
[src > components > ProductListOne.vue]
<template>
<div id='product-list-one'>
<h2>Product List One</h2>
<ul>
<li v-for="product in products" v-bind:key="product.id">
<span class='name'>{{ product.name }}</span>
<span class='price'>${{ product.price }}</span>
</li>
</ul>
</div>
</template>

<script>
export default {
props: ['products']
}
</script>

<style scoped>
#product-list-one{
background: #FFF8B1;
box-shadow: 1px 2px 3px rgba(0,0,0,0.2);
margin-bottom: 30px;
padding: 10px 20px;
}
#product-list-one ul{
padding: 0;
}
#product-list-one li{
display: inline-block;
margin-right: 10px;
margin-top: 10px;
padding: 20px;
background: rgba(255,255,255,0.7);
}
.price{
font-weight: bold;
color: #E8800C;
}
</style>
-----------------------------------
[src > components > ProductListTwo.vue]
<template>
<div id="product-list-two">
<h2>Product List Two</h2>
<!-- same as ProductListOne.vue -->
</div>
</template>

<script>
// same as ProductListOne.vue
</script>

<style scoped>
#product-list-two{
background: #D1E4FF;
box-shadow: 1px 2px 3px rgba(0,0,0,0.2);
margin-bottom: 30px;
padding: 10px 20px;
}
#product-list-two ul{
padding: 0;
list-style-type: none;
}
#product-list-two li{
margin-right: 10px;
margin-top: 10px;
padding: 20px;
background: rgba(255,255,255,0.7);
}
.price{
font-weight: bold;
color: #860CE8;
display: block;
}
</style>

Available to see the full code with syntax highlighting on GitHubGist.

> <li v-for=product in products"> works in the development server. But, it gives me the following error in a text editor:

[eslint-plugin-vue] [vue/require-v-for-key] Elements in iteration expect to have 'v-bind:key' directives.

When v-for is written on custom components, it requires v-bind:key at the same time” (GitHub). “key with v-for is always required on components, in order to maintain internal component state down the subtree (official website).

So, I changed “<li v-for=product in products">” to “<li v-for=”product in products” v-bind:key=”product.id”>".


3) Setting up a Central Store (YouTube)

To install Vuex, enter npm install vuex --save in your terminal for Mac OS and the command line for Windows.

Get rid of the following from App.vue file:

data () {
return {
products: [
{name: 'Banana Skin', price: 20},
{name: 'Shiny Star', price: 40},
{name: 'Green Shells', price: 60},
{name: 'Red Shells', price: 80}
]
}
}

Create “store” folder & save “store.js” file to set up a central store.

[src > store > store.js]
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);export const storeData = new Vuex.Store({
state
:{
products: [
{name: 'Banana Skin', price: 20},
{name: 'Shiny Star', price: 40},
{name: 'Green Shells', price: 60},
{name: 'Red Shells', price: 80}
]
}
});

4) Using Computed Properties (YouTube)

  1. Import the central store => add import { storeData } from ‘./store/store in main.js file.
  2. Indicate that we’d use the central store => addnew Vue({ store: storeData }) in main.js file.
  3. No longer need to pass down any data as props => remove v-bind:products=’products’ from<product-list-two v-bind:products=’products’></product-list-two> in app.vue file.
  4. No longer need to receive data through prop => get rid of props: [‘products’] in ProductListOne.vue & ProductListTwo.vue files.
  5. Reach out and get the centrally stored data => add computed: { products() { return this.$store.state.products; }} in ProductListOne.vue & ProductListTwo.vue files.

Available to see the full code with syntax highlighting on GitHubGist.


5) Getters (YouTube)

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. (official website)

Cut prices in half and put ‘**’ front and back of names by using getters property.

[src > store > store.js]
export const storeData = new Vuex.Store({
getters:{
saleProducts: state => {
var saleProducts = state.products.map(product => {
return {
name: '**'+ product.name + '**',
price: product.price / 2
}
});
return saleProducts;
}
}
});
[src > components > ProductListOne.vue & ProductListTwo.vue]
export default {
computed:{
saleProducts(){
return this.$store.getters.saleProducts;
}
}
}

Available to see the full code with syntax highlighting on GitHubGist.


6) Mutations (YouTube)

Vue has a dedicated plugin that can help speed up your development. It doesn’t replace your logs — it complements them. It’s called Vue DevTools and here are 3 ways you can use it to speed up the development of your Vue apps: 1) Live edit your component data, 2) Debug Vuex with Time Travel, 3) Track your app’s custom events (official website).

> Vue DevTools can be added to Chrome : visit here to add it


Adding <button @click=”reducePrice”>Reduce Price</button> & the following method in ProductListOne.vue file can modify the price. But, the changes can’t be tracked in Vue DevTools.

[src > components > ProductListOne.vue]
methods:{
reducePrice: function() {
this.$store.state.products.forEach(product => {
product.price -= 1;
});
}
}

To track the changes, instead of including the “reducePrice” method from the file, add the method as a mutation property in the central store (in store.js).

The only way to actually change 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 (official website).

[src > store > store.js]
mutations:{
reducePrice: state => {
state.products.forEach(product => {
product.price -= 1;
});
}
}

After that, we need to commit the mutation. To fire the “reducePrice” mutation in store.js, add the following code:

[src > components > ProductListOne.vue]
methods:{
reducePrice: function() {
this.$store.commit('reducePrice');
}
}

Available to see the full code with syntax highlighting on GitHubGist.

Now, we can keep track of changes. In Vue DevTools, you can revert a change or time travel to a certain state.


Also, you can enable strict mode, simply pass in strict: true when creating a Vuex store: const store = new Vuex.Store({ strict: true }).

In strict mode, whenever Vuex state is mutated outside of mutation handlers, an error will be thrown. This ensures that all state mutations can be explicitly tracked by debugging tools. (official website).


7) Actions (YouTube)

Let’s see how we can work with asynchronous operations. We’ll test that with “setTimeout” function.

If we add the function as a “mutation” property in store.js like the following, there’s an issue. After pressing the “Reduce Price” button, actual changes happen three seconds later, but, changes are reflected in Vue DevTools as soon as the button is pressed.

[src > store > store.js]
mutations:{
reducePrice: state => {
setTimeout(function(){
state.products.forEach(product => {
product.price -= 1;
})
}, 3000)
}
}

If we take many asynchronous actions, it would be very hard to track each action on Vue DevTool. To solve the issue, we can use “actions”!


Actions are similar to mutations, the differences being that: 1) Instead of mutating the state, actions commit mutations. 2) Actions can contain arbitrary asynchronous operations (official website).

> Action handlers receive a context object which exposes the same set of methods/properties on the store instance (official website).

[src > store > store.js]
mutations:{
reducePrice: state => {
state.products.forEach(product => {
product.price -= 1;
})
}
},
actions:{
reducePrice: context => {
setTimeout(function(){
context.commit('reducePrice')
}, 3000)
}
}

Also in the “reducePrice” method in ProductListOne.vue, change “commit” to “dispatch”.

[src > components > ProductListOne.vue]
methods:{
reducePrice: function() {
this.$store.dispatch('reducePrice');
}
}

Now, changes are reflected in Vue DevTools three seconds after pressing the “Reduce Price” button.


What if we’d like to pass through a parameter that reduces the price by certain amount? This could be dynamic (e.g., a user can enter the amount). But, in this tutorial, we’re just passing a number.

You can pass an additional argument, which is called the payload for the mutation (official website).

[src > store > store.js]
mutations:{
reducePrice: (state, payload) => {
state.products.forEach(product => {
product.price -= payload;
})
}
},
actions:{
reducePrice: (context, payload) => {
setTimeout(function(){
context.commit('reducePrice', payload)
}, 3000)
}
}

Also, make the following changes in ProductListOne.vue,

Available to see the full code with syntax highlighting on GitHubGist.


8) Mapping Actions & Getters (YouTube)

If we have multiple getters and actions, we could list them like this:

export default{
computed:{
firstGetter(){
return this.$store.getters.firstGetter;
},
secondGetter(){
return this.$store.getters.secondGetter;
},
thirdGetter(){
return this.$store.getters.thirdGetter;
}
},
methods:{
firstAction: function(amount) {
this.$store.dispatch('firstAction', amount);
},
secondAction: function(amount) {
this.$store.dispatch('secondAction', amount);
},
thirdAction: function(amount) {
this.$store.dispatch('thirdAction', amount);
},
}
}

However, if there are many of them, the code becomes too long.

There’s a way to map the getters to the computed property and map the actions to the methods property!

[src > components > ProductListOne.vue]
import { mapGetters } from 'vuex';
import { mapActions } from 'vuex';
export default{
computed:{
...mapGetters({
'firstGetter',
'secondGetter',
'thirdGetter'

})

},
methods:{
...mapActions({
'firstAction',
'secondAction',
'thirdAction'

})

}
}

By the way, ...(the spread syntax) is ES6 functionality. In order to use it on a browser, we need Babel.

Babel is a JavaScript transpiler that converts edge JavaScript into plain old ES5 JavaScript that can run in any browser (even the old ones). It makes available all the syntactical sugar that was added to JavaScript with the new ES6 specification (blog).

Since Net Ninja released this video in October, 2017, Babel stage-3 came out. Type the following in the terminal for Mac OS and the command line for Windows (npm).

npm install --save-dev babel-preset-stage-3

Available to see the final code with syntax highlighting on GitHubGist.


Thanks for reading! 💕 If you like this blog post, please clap👏

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade