Vuex State Management


Centralize your data with this awesome state
management pattern and library, called Vuex. Let’s start right now. Hello creative developer, my name is Mario,
welcome to this video. If this is your first time here and you wanna
learn more about Vue JS, start now by subscribing and clicking the
bell so you don’t miss anything. OK, why you should give Vuex a try? Now, you know how you can transfer data between
two components, which have a direct relationship. This works pretty well for small solutions
and it should also be used in bigger applications. It works great, but if you need to do this
over multiple component layers, it does become harder. And an alternative is to use the event bus,
a central component, a separate Vue instance, which you use to emit on this bus and then
on the target component, you listen on changes in this event bus. It’s better, it’s shorter but it still
has its issues. For example, one bus alone will quickly get
pretty crowded in bigger applications with many different emits and so on. In that case, there is probably a lot of code
and in the end, this may not be the best solution. And it also gives you another problem. The changes you make on your data are hard
to track. You have, in this case, a central bus, but
you access it from all over your application. Therefore it’s really hard to see all the
changes on different places in your application. So yeah it’s better and it’s all you need
for mid-sized applications. There’s nothing wrong, it’s a nice valid
tool. But for large applications with many different
component layers, it might not be the best tool. You need for this case a different setup,
where you have a unique or a central place you can store everything and a place you can
define your way to change data and to get the data. So you have a really clear separation of concerns
and you can easily track the changes on your data and so on. Vuex will offer you a pattern for this, so
let’s take a look. The Vuex pattern or library has the idea of
using a central store, where your state is stored. And that is the key, you have one file in
the end where you store the applications state and that does not mean that your components
can’t have their own state. When you have data or properties in a component
which is only used in this component and only change in it, there is no need to put this
data in the Vuex store. But everything which you use in different
components or places should go in there. With such a central store, you could access
it from every component and change it and then on another component you can simply access
it to get the data. Ok, now how you can use the Vuex Store? Now you will see this here in action. Ok, I have opened up the project from the
last tutorial with the event bus and I would like to rebuild this project and add the Vuex
Store as a central state. If you have not seen the last tutorial, I
link it in the Infocard. First, you need some helpers from the Vuex
package, so let’s install it with npm install –save and then Vuex, this will download Vuex. If it’s finished, you can use the Vuex library. So let’s create a new folder, store, and
I’ll create a new file, the store.js file. Yes, it’s a simple javascript file. And here, what you need to do is first, import
Vue from the vue package and second, import Vuex from the Vuex package. So these are the tools you will need here. And now you can simply tell vue to use Vuex,
to register this plugin basically right. With this, you enable the Vuex functionality
in your application and here you can create now your central state and manage them in
this file. Let’s do this by typing ‘new Vuex.Store’
like this, and ‘Store’ is a method where you can pass an object with the configuration
for that store. Now for holding a centralized state, Vuex
provides a property for this, named simply state. And this is an object where you can define
all your properties, like the local data attribute in components. So let’s do this with the ‘searchValue’,
with an empty string as default value. And now, how you can use this state? Before you can use the store, you have to
export it first right here, with export a constant named store, in which now the store
is saved. Ok, let’s stick to the main.js file and
import the new Vuex store with import store from the store.js file in our store folder. Now with this, the new store is imported and
all you have to do is register it here as a property on your vue instance, with simply
the store property, and as value the imported constant, which holds the Vuex store. And if you use the same name, you can use
the ES6 shortcut, which sets automatically the right key-value pairs in this case. So with that, the store is now added to your
root vue instance and you have access to it in all components. Let’s stick to the first component, the
search component. And here we don’t need the EventBus anymore. So let’s delete this line of code. And in the emit function you can also delete
this line here. With this, you can now directly access the
state in the Vuex store with ‘this.$store.state’ and here the property you want to access,
in this case, ‘searchValue’. And this property gets the value from the
local state in this component. Ok, let’s check the article component. Here you can also delete the import and you
don’t need the created function anymore. So I delete this quickly. For your state can you use a computed property,
so let’s create one. I call it ‘searchValue’ and here you can
access it as before with ‘this.$store.state’ and ‘searchValue’. Last but not least, you can now delete the
local attribute here. Ok, I save now and let’s check if everything
works as before. Yes, it works, that’s fine and improved
a little bit, but you don’t really have many benefits here, right? Hmm. Yes, you store now the state in this central
store and you can directly communicate with it with ‘this.$store’. Already is a lot better but still has some
issues, but you can still improve this. Imagine, you have more components like the
article component, where you want to filter the teaser based on the search value, for
example. The issue is that you would quickly face the
problem that you have duplicated code all over the place and that is never a good sign. It does reach its limits in larger applications
where you do more with the state. There is a solution to this problem. Instead of directly accessing the central
state in the component, where you want to access it, you can create a getter method
in your Vuex store, where you can filter or do other calculations. These getter methods are also centralized
in your Vuex store. Let’s take a look at how you could implement
this. Ok, we want to outsource this computed property
here with the filter into a getter method. Let’s go back to the store.js file and here
you can now add a new property, vuex provides a property for this, called getters. And this is also an object, which holds your
getter methods. And here you can choose any name you want,
I write ‘filteredTeaser’. This function gets the state as an argument,
this is passed by vuex automatically when this function is executed. And here, between the curly braces, you can
do with the state whatever you want. In this case, is the goal to filter the teaser
based on the search value. So let’s copy quickly these few lines here
and here you should change ‘this’ to ‘state’. But there is a problem, you don’t have the
teaser in the vuex store, so let’s put them also in the state here. Now with this getter in place, you need to
access it in your components, right? So let’s stick to the article component again
and clean it up a little bit, things you don’t need anymore. And here, I re-use this computed property
and use the new getter method with ‘this.$store’ and getters and the name of the function you
want to execute, filteredTeaser, but without the parentheses, you don’t execute it though,
Vuex does it for us automatically. Ok, time to save. And you see it still works as before, but
now you have this logic here decoupled with the getter method, which you can re-use it
in all your components. And this is the first awesome benefit you
get. You can use this pattern here to outsource
your business logic, related to your state. But there is a little issue you may have if
you create more and more getters. Now, let’s say you have another getter,
for example, a getter method that returns the total count of all teasers. So I create this method quickly, ‘totalTeaserCount’
with the state as an argument and here I return the length from the teaser array. I want to use this getter in the article component,
so let’s stick to it. And here I define a new computed property
to get the value. I named it also ‘totalTeaserCount’ and
here I fetch the value, and copy this line here quickly and change the method to ‘totalTeaserCount’. And here in the template section, I want to
output this number, so I create a new div element with a class ‘search-results’. In there I want to output some text, Search
Results for example, and now the length from the ‘filteredTeaser’ and here our total
teaser count. Let’s quickly give the new CSS class some
style, just for the look and feel, let’s say twenty pixels to the top, the right side,
zero to bottom and also twenty to the left side. And let’s show the font in bold. Time to save and let’s see if it works. Yeah, it looks good. The problem is that you have now two computed
properties here, to fetch your getters and if you add more and more getters, you have
to repeat that for every getter. That is possible, but it would be better if
Vuex would do that automatically and give us access to all the getters, without having
to set up a computed property for each getter. Well turns out Vuex has a helper for that
you can use. It has a method, which will create all the
computed properties you need and if you want to use this helper method, you should import
it, and the helper is called ‘mapGetters’ from Vuex And now, you can delete this computed properties
here and set the ‘mapGetters’ function as a value, and this takes an array as an
argument or an object if you want to map the getters to different names. I use the array syntax and here you can simply
specify as strings all the getters you want to use in this component. So for example, if you want both getters we
have right now, the ‘filteredTeaser’ and the ‘totalTeaserCount’. That is a much better way to save a lot of
code and it gives you an easy way to access your getters. But it has a big issue. Do you see it? You can’t create additional computed properties for something totally unrelated to your central state, right? And this is not really optimal. ‘mapGetters’ here is the whole value of
this computed attribute. But we have a solution, thankfully with ES6. Let’s put ‘mapGetters’ inside a new
javascript object here, but this will not work, because ‘mapGetters’ on its own
creates an object, with all these computed properties which are methods. Therefore, you have just an object, which
then immediately has an object in itself. And this is not valid code, but with ES6 we
get a spread operator, with these three dots here. This allows us basically to tell javascript,
please pull this object out and create separate key-value pairs for each item of them. With this, you can now add your additional
computed properties here, like this one, and have no issues anymore. Now, this is a great way you can use getters
as computed properties and mix them up with others. Ok, time to take a look at the infographic
again. Basically, you could have the same problem
if you change your centralized state directly into your components. It’s hard to track again, which component
change your Vuex state if you use multiple components. And you would quickly face the problem that
you have duplicated code, again. And therefore that is not really the way we
want to go, especially not in larger applications. So the solution is to use a similar concept
as with the getters, but now for setting the values in the centralized state. And Vuex provides a new property for this,
called mutations. Because it mutates the state, it changes the
state. So you basically commit a mutation from your
component and then this will update the state and all the components listening to a getter
will automatically receive the updated state or the updated calculation or filtering on
it or whatever. OK, let’s see this in action and see how
you can add a mutation. I’m in the store.js file and here you can
add the mutations property, which is an object and here you can now create your own setters. Let’s create one, for the ‘searchValue’
we have in the state, for example, setSearchValue. And this function gets the state as the first
argument, this is passed by vuex. And as the second argument the value you get
from the commit, I named it simply payload. And her in the function you can set now the
‘state.searchValue’ to the new value payload. OK, let’s stick to the search component
to the emit function here, where we set the new search value directly in the central state. And here I want to use the new mutation, right
now. You can do this with ‘this.$store’ and
now with commit, which is a function. And the first argument is the mutation, you
want to commit, in this case, ‘setSearchValue’, and the second argument is the new value,
the payload you want to send. That’s it. Time to save. And let’s see if everything works as before. Yes, looks good. Now if you are creating more and more mutations,
then you have here the same issue as with the getters. Namely the issue you’re repeating this code
here over and over again. So turns out, Vuex provides a helper function,
similar to mapGetters and it works exactly the same way. Let me copy this line here quickly and import
it in the search component, but instead of ‘mapGetters’, you need now ‘mapMutations’. And then you can either replace your method
with ‘mapMutations’ or, you remember, the better way is to use the spread operator,
the three dots here if you want to mix them up with other methods. Now as an argument can you use an array again,
or in this case, I want to use an object to map the ‘setSearchValue’ mutation to the
local function ‘emitSearchValue’, but you can’t here set the value as an argument. One solution is to do that on the click event
here in the template and pass the value. So like with getters, you’ve now centralized
all your changes in one place and you get easy access to these mutations by having the
‘mapMutations’ helper. What a great improvement. Or maybe not? Hmm, let’s take a look. There is an issue, a big issue, or maybe not. The problem is that all mutations are only
run synchronously. That means you can’t run any asynchronous
task in such a mutation. So if you want to set a timeout or let’s
say you want to reach out to a server to get or send data with your backend, this will
not work. But on the other hand, you need this synchronous
task, otherwise, you can’t track any change on your state. Because your state can be changed from every
component and you can’t tell if the order of changes matches the order of mutations,
because some mutations might take longer than others. So that is a big problem. On the one hand, you want to have this reliability
by knowing when you make a commit, this changes the state immediately. But on the other hand, you also need the functionality
to perform asynchronous tasks, like an API call for example. So how you can combine both, asynchronous
tasks with mutations? Now, Vuex provides another property for this,
called actions. And an action is basically a function where
you may run asynchronous tasks and commit one or more mutations if this task is done. And this is a major benefit. You can use Vuex now as a separate data layer
between your backend and frontend, to send your data, to fetch your data and to make
some changes or calculations on your data. Centralized and decoupled on one place. You may trigger an action in a component,
you dispatch it, then you send a request to the server, it takes a couple of seconds and
no changes are committed at this time, the state has not been changed yet. Only if the request to the server is finished,
this point of time, then you commit the mutations and change the data in the store. You are still able to execute some async code
before making some changes. Well, let’s see these actions now in action. Ok back to the store.js file. And let’s use our new property, actions. This is a separate object, like the others
too. And here you can create your action methods,
‘searchTeaser’ for example, and all actions get automatically passed as first argument
the context. And context basically just gives you access
to the commit method, where you can commit you mutations here in the end. And as second argument the payload, you can
pass to this function. And here you can do some asynchronous tasks. Let’s simulate an API call with a ‘setTimeout’
function. I set here quickly the callback function and
the timeout to two seconds. Now with context you have access to the store,
in general, just not to all features but to all you tactically need here. And here I want to commit now the payload
to our mutation, so I write commit, and as first argument the mutation ‘setSearchValue’
and as a second argument the payload. OK, that looks pretty good, but I want to
tweak this function a little bit more, with a Promise. And I want to return it, to respond to it
later in the component. If you don’t know what a Promise is, the
short answer is. Promises in JavaScript are an important feature
that greatly simplifies working with asynchronous code. You make an asynchronous request, for example,
and wait for a result. And whenever the request is finished, you
can continue to work with this result depending on your success or failure. Now here you can set a callback function and
this function gets two arguments automatically passed, resolve and reject. The resolve function is always used if the
action is to be declared successful, reject indicates that something was unsuccessful. I need only the resolve function. Because I can assume that the timeout functions
always work. So let’s copy the Timeout in this Promise
here and execute the resolve function. You can pass here any data you want, I write
simply ‘OK’. I think this is enough here. And now you can use this action, let’s stick
to the search component, where we use the mutation here. By the way, Vuex provides also a helper function,
similar to mapGetters and mapMutations, named mapActions, which you can use here. But in this case, I want to dispatch the action
method manually. So I comment this here out, we don’t need
that anymore. And create a new emit function, where you
can dispatch the new action method. I do this with ‘this.$store’ and dispatch,
and here as the first argument our action ‘searchTeaser’ and as the second the value. That’s it. And if the simulated Request finished, you
can use here a function, then, where you can execute a callback function, if the resolve
function in the returned Promise is executed. Hmm, what can you do here? I have an idea. Let’s create a simple loader. And for this, I define a status ‘isBusy’
and set it to false. And when I click on the search button I set
the new status to true and here when the actions are finished I set the status back to false. Let’s stick to the template and add a new
icon here, I use the spinner icon and add also the spin class for the rotating animation. You can here remove the click event and if
the status isBusy false then we want to output the search icon, otherwise the spin icon. OK, time to save and let’s see how it works. Hmm ok, the complete button rotates now, but
apart from that, the simulated request works really good. So let’s fix this funny button here, by
creating a new div around the icons. And here I want to separate the style from
the icon element. That should work, I hope. Let’s check this. And yes. It looks nice. OK, that was really a lot of stuff in one
tutorial. But Vuex is, in my opinion, a key feature
for large applications. Also, be aware, you don’t have to use it
if you have a tiny or a mid-sized application. Setting up all these getters, mutations and
actions may be too much boilerplate, maybe you don’t want to use it in such a case. It’s totally up to you. But if you want to use this great pattern,
you get an excellent tool. And now it’s up to you to go and build something
cool with it.

Add a Comment

Your email address will not be published. Required fields are marked *