This course has been updated! We now recommend you take the Introduction to Vue 3 course.
Transcript from the "Examining a Vuex Setup" Lesson
[00:00:00]
>> Sarah Drasner: In order to use Vuex, we have to npm and install --c, or we say, yarn add vuex. I typically set it up this way within my source directory, I create another directory named store. Remember, I had created that other directory named component before. This is a preference.
[00:00:18] You could also just make a store.js file and have it chilling in that source directory, if you wanted to. The reason why I put a store directory there and put that single file in store directory is there are cases with a giant application where you might even want to break up that file into several files, and manage the state in a different way.
[00:00:39] If I keep that in one directory from the GetGo, if I get into a situation where I need to refactor, move it into a few different files, I'm already in a place to do that. I don't have to rewrite paths in a bunch of components or anything like that.
[00:00:56] But again that's a preference, you can create that store file wherever you want. So the initial set up in store.js would look something like this. This is the vstore sublime snippet, if you wanted to use sublime. So you would import Vue from vue, import Vuex from vuex. We'd say, Vue.use(Vuex), and import const store and I'm gonna go into more depth on this too.
[00:01:25] I know that I'm like throwing a lot at you right now. State and key and value. So that's the smallest state store you could possibly make ever in the world. This tiny little key value state store. In our main.js file, we'd perform the following updates, so we look at that main.js file and we're gonna look at it in real life as well.
[00:01:52] Import Vue from vue, import App from App.view, would import store from store/store, that was the direct restructure I had mentioned before. And we have store, store. So the things that changed about this file, it's pretty much the same. Here are the things that changed about this file. We have import store from store/store and then we have store/store, that's the second.
[00:02:15] So we can say store/store or we can just say store, because it's redundant and ES6 understands that that's what we might need. In our store, we have a few different things. You saw that we had our state. Our state is very similar to the way that we've been housing our data so far.
[00:02:33] We have data in a function, and we have a few different values that we're using. So state is pretty much the same thing, but it's a centralized place for all of that state. The other things that we have in this are getters. Getters will make values able to show statically in our templates.
[00:02:50] In other words, getters can read the value, but not mutate the state. So you can think about getters as kind of equivalent to computed values. That's kind of how we are using computed values. It's a different view on that same data. We're not mutating that data, we're not mutating the state, we're using getters to have a different view of that state to begin with.
[00:03:12] Mutations will allow us to update the state. They'll always be synchronous, every single time, they're always gonna be synchronous. Mutations are the only way to change data in the store, that is the only way to change data in the store. So you might say, so how do I make asynchronous things happen.
[00:03:30] We will use actions to update the state asynchronously. But we'll always use an existing mutation. So you can't just create an action and mutate the state. You'd have to create a mutation and then use that mutation inside of an action. This can be really helpful if you need to perform a few different mutations at once in a particular order.
[00:03:50] Or, if you need to reach out to a server. It kind of doing it this way is really nice because you solve any kind of sequencing or ordering problems. You don't have a situation where you're executing a couple of things but really while the server, you're waiting to get some response from the server.
[00:04:06] Something that was suppose to follow is being executed, you can kind of make sure that you're executing things exactly when you need to be. So mutations are like, how we've been using methods. They're any kind of change to the state that needs to occur. And actions will take an existing mutation and apply some sort of asynchronous activity, either reaching out to a server.
[00:04:30] Here, we're gonna use set timeouts just so you get the idea of them. We separate actions and mutations because we don't want to get into an ordering problem. All right, we've got a basic abstract example, we're gonna walk through this really quick. We had kind of talked about that store, and we've just said key value before.
[00:04:50] This is kind of like building off of that example. So in state, we'd maybe have something like counter is 0. This getter again, like the computer had value, this getter is allowing us to use this tripleCounter and it's really saying state.counter * 3, whatever kind of logic that we need to use it for.
[00:05:14] Mutation is mutating a state, mutations are always synchrounous. When we mutate the state with an argument, we usually call it payload. That's just like industry standard so we're showing it passed with payload, we're passing in the state and then also num, which is the payload and we're incrementing that value by the num here.
[00:05:37] Now this is a lot. And I'll use the next couple of slides to go into much more detail about this so that you don't feel like I'm overwhelming you with just this information. And actions, we're showing it passed with a payload, represented as asyncNum, which is an object.
[00:05:56] So we've got this commit. We're passing an asyncNum, we have the setTimeout and this asyncNum is an object so we can say asyncNum.by and asyncNum.duration. It can also just be static amounts if you want to. So okay, let's go a little bit further into actions cuz that was a lot of things for you to understand very, very quickly.
[00:06:21] We have to pass on the context in actions. Context, we'll use context.state, context.getter. The reason why we're not using context.state directly is so that we avoid collisions because there's a more complicated reason for this but we have different modules, and then it can be name spaced differently. So in order to keep those from colliding in actions, we have to pass in context.
[00:06:48] But we're actually gonna abstract the context away. Typically, we don't use it like this. What we'll do is destructure it and just say, instead of that, we'll pass in in parens commit, and then say commit increment. So that's a little bit more legible, a little bit clearer to read as well.
[00:07:09] If we're going to do some sort of asynchronous calculation from this, so at first, we had that commit, we're passing in commit increment, we have commit. We have setTimeout, and we're committing increments. So increment would be a method that we've already created. If we go back, increment was something that we made in mutation.
[00:07:31] So we're taking that mutation and mutations always have to be committed when we start to use these in our component files. We're committing the mutation in our templates, and we're also committing the mutation in our actions as well. And so, we're committing this increment. And we have it happening after one second if you note setTimeout, we are waiting for one second and then we're doing that mutation.
[00:08:02] If we just did the mutation and let's say, it's something that takes a long time and we are not using it asynchronously, it can actually block up our application because they can only be synchronous, and we want to avoid that. Those actions become really useful for those mutations because we don't want to block up our application waiting for something to happen.
[00:08:24] So if we're gonna use this action in our actual component, we use a thing called dispatch. I will go over this part again, so I'm going to go over it here and then we're gonna go over how they're each used again in a second. We have methods and then we're going to say asyncIncrement, I'm just calling it the same thing, I could call this something else, I could call it go get tacos.
[00:08:51] And then we have this.$store.dispatch (asyncIncrement). So I'm calling this and what it's going to do is it's going to then reach out, and then it's going to set a time out and execute, and commit that increment after waiting one second.
>> Speaker 2: Just a comment on the actions.
>> Sarah Drasner: What's that?
[00:09:15]
>> Speaker 2: So on the actions, are you kinda using setTimeout as an illustrative thing to show-
>> Sarah Drasner: Yeah, just because it's a little bit easier than also showing all of the-
>> Speaker 2: Like doing an HTTP request is what you might be doing.
>> Sarah Drasner: Yeah, it's a lot to understand in general.
[00:09:31] So the setTimeout is kind of abstraction so that you're not bogged down with some other details.
>> Sarah Drasner: So let's say, we need to change some of these values, maybe we need to pass in a payload. If we needed to pass in a different duration, here we're just hard coding that one second.
[00:09:53] But let's say, we need a different duration. We would then create this payload here, and we say duration. And then, we would create an object here where we say duration is 1,000. So that's how we updated in those two places. So here, it's hard coded, we're just calling asyncIncrement.
[00:10:13] If we want to dispatch with a payload, we have duration, and then we're passing in an object where we have duration, yeah?
>> Speaker 3: If the object that you're passing in has only one property, does it just somehow pull that single property? Cuz it seems to me you're passing in an object with the-
[00:10:34]
>> Sarah Drasner: Yeah, I mean, we can do it either way. We can pass in just the one but if you need to build off of it in anyway and create more values, then it becomes easier to then kind of update from that point. So if we need to do something like this object.
[00:10:55] And that's actually kind of a typical used case I would say is that we're not just passing in one argument to this, it's a little bit easier to update that.
>> Speaker 3: It looks like this exact example and looks like you're passing an object that's the second parameter to setTimeout?
[00:11:12]
>> Sarah Drasner: We are, we're passing in an object and that's going to be the duration. So this is destructuring, it's an ES6.
>> Speaker 3: So, it is destructuring right now.
>> Sarah Drasner: Yeah, it's destructuring. And so then, we can say asyncNum, we pass in asyncNum, and then we can update this to by and dur.
[00:11:33] So then we can say, were asyncNum.by, asyncNum.dur, and then we can update it in that way. So the way is that we would use all of these things that we just talked about is on the component itself, we'd use computed for getters. This makes sense because it's kind of similar to computed values anyway.
[00:11:58] We have our computed and we can use value directly in our code. We say return this.$store.getters.tripleCounter, or whatever you want for that, example. But we basically use that as a computed value because that's basically, where the same kind of dance that we are doing is that different view on the same state.
[00:12:21] We'd used commit for mutations. So anytime we use a mutation, we use commit, even if it's in the component, even if it's in store.js. And then for asyncIncrement, we us dispatch for the actions. So any time you use an action in one of our components, we use asyncIncrement.
[00:12:43] So I'm gonna bring up this example where we're doing a few different things here. So we've got,
>> Sarah Drasner: I tried to separate these out as much as possible so that they were easier to understand. So if we go into vuex example, and I'll do subl first. And,
>> Sarah Drasner: Here we have the state from the store is zero.
[00:13:21] The getter for tripleCounter from the store is zero. So that's that getter that kind of computed value. Let's increment by two with a mutation, we're gonna increment by two, you can see that that getter is changing but it's always gonna be times three. Let's increment by 10 with an action async, so we wait one second and then we increment by 10.
[00:13:45] And this is a really simple adjustment store state with the component. I would never do this, but people do do this. So I'm showing it to you, I don't think that you should do this. But I've seen it in enough applications now that I wanna call out to you, it's possible, but I don't think that that's how you should be working with it.
[00:14:04] So here we go into the source, and we're going into components. And I made a separate component for each thing that you're gonna do so that it is a little bit easier to understand. So here's our store directory, like I said. We have our store here, and it's pretty similar to the stuff that we mentioned that we would be working with.
[00:14:24] Then, in our main.js file, remember I said, we're gonna import store from store, here that is, and there is the store here. And then in the action, let's start with the app. So I can say, the state from the store is span, and then state and then in here, in that computed value.
[00:14:48] Again, we're just accessing the state from the store, but I'm still gonna use a computed value because that computed value's going to be cashed until the dependency has changed. So it's a good chance for me to use computed value, just returning that state from the store. So, I'm gonna say state return this.$store.state.counter.
[00:15:08] And it's going to be this, and then $store. And we're able to use state. Remember, we saw that earlier with getters. We can use the state right here. Then, in the getter, we have something very similar. We have a computed value, we have triple this.$store.getters.tripleCounter, and then we have triple passed in here.
[00:15:31] And then, in mutation, we can increment two with a mutation button @click is increment. That's gonna call this method here. And the method is this.$store.commit increment, and then we're passing in something like two. There's many different ways that we could actually use this mutation, that's just an example.
[00:15:52] And then in the action, we have, let's increment by, this should actually say ten, within action async, button @click asyncInc, and then in our methods, we say asyncInc this.$store.dispatch asyncInc, and passing in 10. So if we go back to looking at that store, you can see that we're actually committing this as our payload.
[00:16:19] This is our payload. Those are the things that we were passing in. So that is all organized in the repo for you. We just went over all of this kind of stuff. So the getter is, I've kept these in all in different slides for you. The getter, we're using a computed value.
[00:16:37] We're saying, this.$store.getters.tripleCounter. And then in store.js, we are using whatever logic we would like to do for that different take on that value. We have a mutation methods @click increment. An increment we say, this.$store.commit. Remember, mutations are always committed and actions are always dispatched. So we're always going to commit it this way, in store.js.
[00:17:06] In async action, we've got button asyncInc, and we've got $store.dispatch, and this.store.js. And there are times when you have a lot of stuff that you need to get from the store, and rewriting out new values for each one of these can get a little arduous and painful and not at all dry.
[00:17:28] So Vuex allows us to do a thing called map getters, mapActions, map mutations, so you would say, in the same file at import mapActions from vuex. And then I would be able to, with a spread operator, say mapActions, and then say, all of the things that I wanna go get from that source.
[00:17:53] So I don't have to keep rewriting all of those values. Some people just write it like this right away. So this map this.increment to this.$store.commit increment. Actually that will be dispatched for actions. So this allows us to still make our own computer properties if we wish, because we could use these and then reuse them and create another method below them that you uses that value.
[00:18:21] So we can make new computed properties from these as well. If this doesn't fit our exact use case, so that's pretty great. One thing though, is that spread operators are not supported in all of these even in newer babel necessarily. It depends on your build. So you might need babel preset stage 2, or babel plugin transform object rest spread.
[00:18:46] So you might have to yarn add, or NPM install one of these two to get this working correctly. So if we're working with this, if we're working with mapActions before we were doing that thing where we were increment and then comma five or whatever in that order to pass the payload.
[00:19:09] In this instance, we'd write @click, and then the increment, we'd pass that in parens instead, because we're not kind of using it exactly the same way, so we pass the payload this way. And then store.js, that's the same test there.