Vue 3 Fundamentals State Management with Pinia
Transcript from the "State Management with Pinia" Lesson
>> So we're gonna talk about global state management with Pinia. Pinia, if you don't know, I believe it's French for pineapple and basically it is a successor to Vuex. So if you've heard from the React community about Redux patterns and those kind of thing, Pinia is the evolution of that because Vue did take inspiration from the Redux pattern.
[00:00:19] So we had Vuex for a very long time. But there were some ergonomics with it that made it kind of tricky to work with over the long period of time. And so Pinia with the composition API has basically been the evolution of Vuex. Okay, so in terms of Pinia, let's take a look at what that looks like.
[00:00:36] All right, because global state management typically is a fairly complicated topic. So first of all, be sure to pin their docs, pinia.vuejs.org. Once again, this is an officially supported view library. And it's got this cute little mascot, which I need to get some stickers of. And basically, Pinia, funny enough what started out as just like an experiment that Eduardo also known as Posva, who actually wrote Vue Router was kind of experimenting with how we might reinvent Vuex.
[00:01:07] And so he kind of came up with this idea and it's gained a lot of popularity and so thus it is now the official one. Okay, so the show that actually we're gonna go ahead and do is on the playground. I'm gonna go ahead and just install it right now.
[00:01:21] So install npm install Pinia. And so once again, let's go and see how we know that we're using it. We can go ahead and look inside of the package.json. And as we see here, we'll see vue-router, and we see pinia now. Now, the only thing we need to do before we need to continue using it though, is just like the router.
[00:01:41] We need to tell vue that we're using this plugin basically. And so inside of our main.ts, we get the opportunity to now create basically a pinia instance. How does that work? It's just like vue router actually. So we can import createPinia from pinia. As I just show you that again.
[00:02:03] This time it actually autocomplete, there you go now that's right. And so remember that multilane thing I was telling you about. So this is where this is super helpful. Because we can say const pinia = createPinia and then after that after we use the router, we can just say app.use(pinia).
[00:02:20] There you go. And just like that you have Pinia enabled on your app. So, how does Pinia actually work, right? Creating stores and stuff this is stuff again is typically very complicated. But I think I've set you all up to kind of see what it's actually like. What we're gonna do is we create a folder called stores.
[00:02:41] Because what are these, after all? What are we trying to track? We're trying to track stores of data. So what are we creating? We're creating a directory for stores, why? Because when it comes to global state management, they're typically referred to as stores of the data of the actions we need to take in order to make changes to those data stores.
[00:02:55] So, let's go ahead and insert this playground this time. We notice that we have this count store over here. Let's go ahead and recreate this over here. So I'm gonna do CountStore and I'm just gonna do it in Pascal case just to make it look different. And what does that look like now inside of Pinia?
[00:03:12] Well, let's go ahead and define the store. As we look at the docs here, we're gonna do things very composition API like. We're gonna import defineStore from pinia. And then what we're going to do then is export a const of use whatever the count store or whatever the store name is.
[00:03:29] So useCountStore is typically what is like what is called. So use appended to this. And then we call the defineStore in here. Now the difference is, is one, we first start by defining how we want like the store to be labeled. So I usually just call it countStore, like whatever it's already called inside of the file name.
[00:03:47] And then after that you get an object that you get to configure. And inside of here, it's gonna look kind of familiar. So one, you get a state option to return data. There is a getters option. And there is an actions option. And so you might think, this looks familiar.
[00:04:09] This looks a little bit like options. And it does in the sense that it's providing you structure at how you manage your state. Guess what we saw the composition API earlier is that you can kind of wrangle together whatever you want. But this, especially when we think about global stores, it's actually useful to have a convention for everyone to follow.
[00:04:27] Because you don't really don't want it to be like the wild wild west when it comes to people managing and accessing data. And something else that you might notice too, though, missing from here especially for my fellow Vuex users, there are no mutations. And that is probably one of the best things about Pinia is that, when we have something in here like count zero and we want to increment the count all we got to do is go this.count ++.
[00:04:53] And it takes care of everything for you. No more creating this separate thing that's like a mutation that requires you to call in action and then the mutation is basically redundant of the thing that you're trying to do. That's all gone. And so if we think of this, another way to alias it, just to provide that mental model Data > Computed > Methods.
[00:05:17] That's what you have. And so if we compare this over now to the CountStore that we originally sort of scaffolded ourselves together inside of the composables. You'll see here how different the code is in the sense that this is just kind of however I feel like, and however I chose to write it.
[00:05:34] But you get an additional benefit, actually, that I'll show you in right here. So let's say, let's add the increment amount to 80. And then this one is going to be += this.incrementAmount, because this should look familiar to our base countStore that we've done before. And then we'll have a doubleCount here, that will be a function that receives the state as an argument.
[00:05:54] And we just return state.count times two, just like that. And then I mess something up because I use the comma instead of a period, boom. Okay, how is this any different than what we just did? So we have this right here, and I'm going to leave this to the right so that we have this as a reference.
[00:06:11] Now, inside of our base counter. This time what I'm gonna do is we're gonna go ahead and actually import this store. So import useCounterStore or useCountStore, it looks like. There you go. It actually detected it properly. So we are getting some type auto completion, which is super nice.
[00:06:29] And this time, we'll do a const newCountStore, which will be the useCountStore like this. Just like that. And then we're gonna return newCountStore, just like that. Okay, so what do we get out of this? One, I'm gonna show you what it looks like when we actually use it.
[00:06:49] So let me bring that back over here, this is great. Okay, so first thing is that now that you have your CountStore. What you actually end up finding out is that when we look at something like Third Counter Version. So when we look inside of here, you're gonna notice that we get auto completion here.
[00:07:15] It actually can detect properties on that store now to get a lot of auto completion out of this. So in other words if we take a look, increment is actually sitting right here. It actually knows that it's a property now. And so there's a lot of work being done underneath to help with the intelligence of what's going on when you're managing your code.
[00:07:34] And so here, we can do count, for example, and then as you can see, it's zero. But actually very easily if we added a button, we can just say, newCountStore.increment. And then New Increment. Then you can see, it just works. And the reason why I kind of preface this with how global stores are normally complicated.
[00:07:57] This should actually remind you of the composable we kind of designed ourselves. You have this thing called CountStore. Again, it learns from the options API takes the advantage of the option API by providing some structure. Gives you the chance to manipulate it as you see fit. But then when you call it, it feels just as natural as using any other part of the data.
[00:08:15] Whereas in a lot of other state managements that I've used before there's usually a lot in terms of like going in to fetch a certain thing and then auto completion becomes a bit of a pain. If you use Vuex you knew that you had to type the string exactly a certain way to initiate a certain action.
[00:08:29] There was a lot of opportunities for mistakes. But with this, it's easy for us to understand what's happening. And on top of that, I'm always a big fan of conventions, especially when it comes to debugging. We know that state management is notoriously difficult to do. And so, if you take a look right here, you'll notice that another benefit you get by using Pinia, is that you get this dev tool right here.
[00:08:53] And we can actually go ahead and start inspecting it and doing stuff just like you normally would want to do when you're debugging a large application. And the other thing that again it feels really natural as we do this right now is that unlike a global state management store like the way Vuex used to be, you used to have to intentionally namespace modules.
[00:09:11] In other words, you have to be like from this global piece only take this small piece and import it here. Only take this small piece and import it here. But then sometimes you still ended up with like importing everything at once, right? So Vuex stores became rather cumbersome and people had trouble with that.
[00:09:25] But with this new model of like a compostable about Pinia, this is actually modular by default. You only import the stores you need where you need them. And so almost without even thinking about it, you've solved the performance bottleneck that might have naturally occurred in an enterprise grade app.
[00:09:41] Where you have the singleton tracking global state, everyone just chucking stuff in there because they can or is the easiest thing to do. But now it almost is just as easy to go. Wait, my functionality is about login. I'm gonna create a login store. I'm not gonna toss it in the global state.
[00:09:56] I'm gonna actually think of it modular first. And the fact that it's namespace modular first, I think that's wonderful because it encourages us to use best practices without even realizing it. And they are some of the best sort of advances that we can have when it comes to developer toy.