Welcome and demo app overview
Welcome to this video. Welcome to another video about Vue.js. In this video I'll introduce you to Vuex, a nice add-on to Vue.js which makes state management a whole lot easier. Now why do we need to make it easier though? Well, I prepared a little example application, which you'll find a link to in the video description, which will show you why you might quickly reach the point where you need kind of a better state management than doing it like here.
Running the example and current state pattern
So let's take a look. I opened this project, which again you can find in the video description, and there I need to run npm run dev. This was set up with the Vue CLI, about which I also have a video on this channel. If we have a look at the running application we see we got two sections here, basically two components, and one we can register and in the other one we can then unregister. As you can see, if I click here the counter goes up and we see the date here and everything is great. Well, it's kind of great. Let's have a look at the code. It's a very simple application. As you can tell I got only two components and my App component here. In the App component I'm basically managing my whole state. By state I mean I manage my registrations here and my users, the total number of users or all the users I have. Then I manage the state by, for example, if I click in the registration view file where I can sign up, if I click on this register button here I fire the registerUser method. This one here emits an event userRegistered and passes the user. It also sets the registered state on the user to true and the user gets into this component in the first place because the user is part of this users array which again is passed into the registration component here as a prop.
Prop/event flow and pitfalls in larger apps
So back to the sign up: we're emitting this userRegistered event and I'm listening to this here in my App.vue file. When it's fired I'm triggering the userRegistered method here and there I'm basically setting a date and I'm pushing a new registration on my registrations array. I'm basically doing the same just in the opposite direction when a user unregisters; then I'm firing the userUnregistered method and so on. So this is basically my state management taking place in the App.vue file and I pass props to the other components and I listen to their events. Now don't get me wrong there's nothing wrong with that and for a very tiny application like this one that's just perfectly fine. But imagine the same in a bigger application where you have even more components interacting with your users, interacting with your registrations and also with other parts of your application state. State basically refers to the variables or the values of certain properties, if you want to call it like this, you want to keep around because multiple components use them and the user interacting with your app changes them. For example I change the state by clicking on register here or unregister. If you imagine that in a bigger application it quickly becomes very difficult to manage. You have a lot of props and events to which you're listening and also you got a lot of different parts in your application emitting these events changing values. It could quickly become difficult to see what changes things where and so on. Therefore we have a better pattern, actually a whole app which handles this, and this is Vuex.
Installing and creating a Vuex store
In this video I want to show you how to get started with Vuex, how to set it up and how to use it in its very basic and not yet final form. In the next videos we'll enhance this application and use more and more features of Vuex to finally get to a point where we can say yes now we're using Vuex as it should be used and our state management is much clearer. So let's get started and let's set up Vuex. For that I will go to the console to the terminal and run npm install -D save and then it's simply vuex, that's the name of the package. This will pull it down, add it to my package.json and install the 2.0 version which is the version which I need. With that I now need to set it up. I could do that in my main.js file but a better convention is to do this in a store.js file. Why store? Because Vuex is all about having a central store which holds your state and which allows you then to manage this state in the end. Therefore I call the file store.js and I will create a store there. Now to create it I will first import Vue from 'vue' and I will also import Vuex from 'vuex', this newly added package I just installed. The first thing I got to do is I have to add Vuex as a plugin with the Vue.use method here. This will unlock some additional properties and methods I can use. Then I need to set up my store. I will export it because I want to use it outside of this JS file and I'll name it a constant named store. The store is then created with the new operator and then Vuex.Store. This Vuex package we're importing up here has this store object or a constructor we can instantiate here. Now to the store I need to pass a JavaScript object configuring that store. The key thing every store has to have is the state. The state property is a reserved word Vue.js or Vuex will recognize. This is my state and this is now also a JavaScript object and here I could have my users and my registrations. Let me quickly grab them from my App.vue file. I'll cut them, store and add them here. Now I'm setting them up here with initial values, so the empty array for the registrations and the array of user objects for the users. With that I'm already using a store. Of course the app is now broken but we'll fix it.
Registering store in main.js and accessing it
I need to go to the main.js file and of course here I need to import my store. So here I will import { store } from the store.js file. Then I need to add that store to my root Vue instance. I do this by simply adding store: store, or since we're using ES6 I can also take the shortcut and just add store and it will automatically assign store to that since it's the same name. With that the store is set up in the store.js file. We registered the initial state of that store and we added the store to the main root Vue instance that manages my whole app. With the store being registered there we can now access it from inside the application. So let's do this. To access it I will start with the registration part where I can sign up in the registration.vue file. Right now in the registerUser method I'm simply emitting an event and I'm getting my user as props here. Well, we don't really need to follow this pattern anymore because the App.vue file which is right now passing users via props and listening to the event doesn't need to do that since our users and our registrations are in the central store anyway. We can just access this store directly. To do so I'll start with my users. I'll remove the props in my registration.vue file and instead add a computed property in the computed property of my Vue instance of this component. You can give it any name; I'll stick to users since this is the name which I'm using up here in this v-for loop. Users again is a computed property and all I do here is I return this.$store.state.users because $store is a special property you can access. This is added because I'm registering Vuex here with Vue.use. $store gives me access to the store, then I access state and here my users because state.users is what I'm setting up here. I'm now accessing this array of users in my registration.vue file. With that accessed I can go to my App.vue file and get rid of this prop binding where I'm passing the users prop. I can also already get rid of the event because I will also change the way I handle this. In the methods I don't need to emit this event anymore. I can still set user.registered to true right now but what I want to do here is I want to also access the store, the state and registrations, this empty array at the beginning, and there I can simply copy the logic from my App.vue file where I also have my userRegistered method. I'm copying that code from that method into my registration.vue file and now I don't want to call registrations.push, instead I want to push it on the registrations in this state in my store.
Direct state mutation, syncing components, and final fixes
I will say one thing right away: manipulating the store and the state in the store like this is not a good practice and not how you want to do it in bigger applications. I will of course in the next videos show a more elegant way, but for now this will do. With that I'm changing the registration state. I can now get rid of userRegistered here in methods in my App.vue file and I can now also go to the registrations.vue file and implement some code here. Instead of getting props I'll also add my computed property here and here I also want to get my registrations. Registrations is also fetched from the store state and there the registrations are just like that. I need to return it though, so this gets the registrations from my global store. Now when I unregister I can go back to the App.vue file and copy my userUnregistered code like this. I'm going to the registrations.vue file and I'll simply paste it in here. I need to make some adjustments though. For one, where I find the user which was changed I need to access this.$store.state.users and find the user by its user ID which is saved in the registration. I can then switch user.registered to false again. The registrations.splice where I remove a registration I need to access this.$store.state.registrations and the same is true for other references to this.$store.state.registrations. I also need to change the computed total property here; I need to go to my store state registrations and fetch its length. With that all the logic is in the two subcomponents and I can get rid of the methods here, get rid of my computed property here, and also get rid of data which I didn't use. I can also get rid of the prop passing and the event listening in my App.registrations component. Let's see if this works as it should. It probably won't because it seems like I got the import in the main.js file wrong. Indeed I'm exporting a constant named store here; I'm importing something like if I had defined it as the default export, which I could do, but since I created a named constant I need to wrap this in curly braces. This is just ES6 import syntax. With that if I save this, let's see, this looks good. The application seems to run. Let's click on register. Well, it goes up here but we don't really see the registrations in the registrations component and we also get an error that property or method registrations is not defined. This happens because here I got two computed properties set up that of course won't work, so let's merge them into one. That was a mistake on my side. Save it. Now we should see a working application. Indeed I can register users, I can unregister them. The missing piece though is they don't really disappear here on the left; they somehow don't seem to get marked as unregistered. The reason for this behavior is not a bug. The reason is here in my computed user property which defines through which users I'm looping. I'm always returning all users. Instead I need to add a filter here and filter if the user actually is registered or to be precise if it is not registered. Only if user.registered is false shall it be returned in the user array in the end which I'll loop. Now finally we have an application which works as it should now with Vuex and the central store and state. What got better compared to the start of this video is we stripped out all the code from the App.vue file which makes this file leaner. The code shouldn't be in there to be honest. We basically put the code into our two other components but the important thing is our state — the registrations array and the users array — is kept in one central state: our store here which we access from both components. We don't have to emit events and pass props and so on. This is an enhancement. Of course as I mentioned at the beginning of the video for small applications the setup might actually take longer than simply using the props/event pattern, but especially in bigger applications you will see the advantages of this approach. In the next videos we will refine this approach and add some best practices because the way we're doing it right now isn't really the best practice, but I'll come back to this in the next videos.