Production-Grade Vue.js Vuex Best Practices
Transcript from the "Vuex Best Practices" Lesson
>> Let's talk a bit about state management, so again, for those who are newer to view, the official state management library is Vuex. Think of it like the Redux pattern, where you're able to define the you have your state and then we have getters mutation actions and basically a standard for how to update global state.
[00:00:18] And this was basically the convention for most of you to apps in the past. So the first question most people will ask when it comes to Vuex is what data should actually go into Vuex? Because now that you have this shared global state, it's easy to just put everything in there because then you can kind of access it from anywhere.
[00:00:36] But in reality, we want to try to encapsulate things as closely scaled as possible and keep the global store as basically as simple as humanly possible since we don't want everything just polluting the global space. And so examples of this include things that you want to keep between router views.
[00:00:55] And so as you move from path to path, there may be certain data you want to track regarding user preferences and those sort of things. Those are great examples, something that should be global and for example to the first point when you have components that don't share data, that don't have that direct parent child relation.
[00:01:13] This can also be a great example of when the US could be a good example but certainly as we saw the composition API, now, it might be better to actually refactor that and just make that a shared module. But, we will go into the pros and cons later, about why that's not necessarily like the way to do it.
[00:01:29] But these are the basic principles, that go into, whether or not something should be in Vuex. Now, the other thing to note about keeping data between your router views, is in the example here we have is that you have a list of records fetch from an API, is that while you might track what the users search for, for example, right?
[00:01:47] Like user search equals this, it's still really important to still keep the route params updated inside of your app, architecturally speaking as like the global source of truth, rather than the global store. Because this gives you two benefits as far as one, ensuring a consistency as far as user experience when they search for something they actually have the parameters update.
[00:02:07] And that parameter is what serves as the source of truth for what happens but if you need additional information that goes beyond simply route params view, it can certainly be a good place to start. And so any sort of global state as we mentioned regarding like login status, you know, the user information or any sort of global notifications, absolutely Vuex is a great case for it.
[00:02:28] And what boils down to it right is that if you think that it will make managing the state simpler global state is a great way to sorta have that escape hatch to share things. Now, what to avoid putting in Vuex, right? Because that that was some unlike the generic principles and so user interface variables are things that we wanna try to avoid.
[00:02:49] So when you're tracking whether or not the drop down is open, unless this drop down impacts the entire application somehow, it's best to keep it scoped directly to that component that will then have its own internal state regarding whether the drop down is open or not. And so again, things like his input focus is most visible, these are all things that really should be contained to the component for the most part.
[00:03:10] There are as always, there are exception form data validation results all of these are also good examples of keeping it encapsulated to the form. If you have a credit card payment form, keep all the stuff in there don't pollute the global namespace especially if it doesn't need to be shared, especially for things like forums, which honestly are usually localized to that one piece.
[00:03:31] And so if you're like, well I have multiple pages on my form, I need to know like the result between all of them. Then now it'd be a great example of creating a parent form that then like controls how all like the individual pages go. Instead of like creating multiple forms that talk to the global store and then shoot everything down, so another architecture pattern to consider.
[00:03:52] And so a lot of times see when we're pulling stuff from APIs, it might be tempting to store single records from the API in there because the user favorited it or something. But often times when you have this sort of things you generally again still trying to keep it within the component, only when there's some sort of global impact regarding a user preference to those things then we then go into the global state.
[00:04:19] And so this is a question that actually I've seen this in a number of code bases where there's this question of like, do I always need a getter in order to return a fragment of state? And the short answer is no, really, when you're thinking of Vuex getters, you should really think of them as computed properties.
[00:04:38] And so when you actually need to access the state of your Vuex, basically store you can do that directly. There's like people I've seen people write getters to get states but if you're not doing any sort of compensation computation, you sort of it's a redundancy. And actually again, if you can access it directly just like you wouldn't create a computed property to access something in data, you create a computed property to run calculations on data.
[00:05:04] Similarly, this is how you need to think of getters as well, if you're not running a calculation on something, you don't need to getter, just access the state directly. And yes, okay, so we just covered this regarding derived Vuex state so as far as derived Vuex state to give you a little bit more context.
[00:05:20] If you're storing, like the response from an API and you have a list, and for example, like within this list, we only want to filter down the items that the user favorited, because every object for example, has an attribute of favorited true. Then this would be a good example of where you would go like getters where it's like API, favorite API lists or favorite items list.
[00:05:47] And that would be computed based off the state of your entire like record list. And that would be a great example of how you should use getters to make maintenance easier across your application. As far as the tip goes, when it comes to using Vuex I strongly recommend avoiding the calling of mutations directly in your components.
[00:06:10] And so this is a little bit of a debate as well and a controversy between or night, I wouldn't call it controversy. It's just there's certainly a debate, because a lot of times one of the big criticisms of Vuex and the standard Redux pattern for that matter is when you have mutations which actually update the store directly.
[00:06:25] And then you have these actions, the writing an action in order to commit a mutation, especially when it's so one to one like this can feel very boilerplate and so a lot of times we don't wanna do that, right? We just wanna increment count, we don't wanna have to write an action that's called add change amount to count in order to commit an increment count.
[00:06:45] It seems like a lot of boilerplate but the reason why I would recommend avoiding it, as much as humanly possible is because going especially as Vuex begins to update, there is already talks in Vuex 5, for instance. And so actually let me back up a little bit, currently we are on Vuex 5, 4, view 2 and so for view 3, they are porting over the existing Vuex functionality into the Vuex 4.
[00:07:10] And then, Vuex 5 is going to be basically and evolution on the standard, once Vuex as it stands, is stable in the view 3 ecosystem. And so, in view 5 and some of the ideas being explored, really is that We might actually be able to detect when you actually want to do mutations without having to write everything.
[00:07:29] And so what this means is you can just call your actions and then basically programmatic, like the algorithm will know which things you're trying to mutate in the state. You don't and so basically like, you can imagine that mutations are just gonna but they're going to disappear and so that's the idea that we're working towards.
[00:07:44] And so if it turns to like patterns going forward in the long term, even though it's a little bit more boilerplate, the fact that mutations has like there's again the ideas are being discussed of like going away. This means that you will have one less refactoring to do as you start to upgrade your application like the migration path is much simpler.
[00:08:04] And more importantly when it comes to using actions, it does allow people to easily enhance like that functionality. So while it might seem like a one to one like mutation at one point, allow keeping it in an action allows other developers when they're working on the same feature to then go ahead and enhance it.
[00:08:19] Because, as we all know, when we're working on enterprise level applications, you never know which piece of the application you're gonna be working on before you move on to something else and someone else has to take over. So in another way, this is just like, in some ways, like a courtesy to future developers that hey, I know that right now this is only doing a mutation.
[00:08:37] But if in the future like you need to do anything piece of cake, I'm already calling the action in these places, you can just enhance it as you see fit. In this regard, this is similar to the dot view files where I was saying keep all your HTML in the single file components.
[00:08:50] Because when you actually need the CSS when you need j s, it's as simple as adding it to the view file rather than having to create new files and reconfigure everything. That said, in case you don't know, Vuex also has a bunch of built in map helpers. And so the pro tip here is to use it but being consistent with what I just told you, I'd like to particularly avoid mutations if you need to call them and so the reason for this is you'll see that.
[00:09:18] What map helpers allow you to do, basically at a high level is it allows you to take stuff from your Vuex store and then sort of rename it so that it's easier to call. So rather than having to call like this.state.user.name, it would just be this.username, that's like an example called map state helps you.
[00:09:35] And then similarly, the map getters instead of now having to access this.store.state.activeusers list is just this.active users list. Now, the reason why, so you can see there's the methods down there that allows you to then call the action as you would a normal method. But the problem is the reason why I'm saying to avoid using mutations inside of the map mutations, like let's say you have a really strong use case for mutations, I believe there is an argument for that out there.
[00:10:03] The reason why I'm saying not to actually use map mutations is because you want to be as explicit as humanly possible that you are mutating the global store. And so being verbose with the this.stored.state.commit like that whole thing is like it's basically what I'm trying to say is it's valuable to make that clear to developers.
[00:10:22] That this thing that you're either you're changing or updating this thing is changing the global state, which means the side effects are basically like, you have no like there are loads that can happen. And so in that regard, that's why I think not mutations has in that regard, I would recommend avoiding it and then again, if you really need to mutate please just call it directly.
[00:10:46] Okay and then as far as the global state goes, in case you didn't know Vuex also allows you to do namespace modules. So rather than keeping everything enclosed in a single like global state, you can be like, okay, there is a state and there's a module in here that's like tracking the shopping cart.
[00:11:05] And that way shopping cart has its own state getters, actions and mutations and then we have another one for to do list, which has its own global state. And so generally speaking, when it comes to enterprise level applications, even when you're just starting out, don't even wait for the data on this particular one, just enable namespace modules.
[00:11:22] And so we've covered a lot of different techniques here and so the reason I'm not going to that is because If you haven't checked it out already Divya. She has a great Vuex workshop here on front end masters that I highly recommend checking out. So in there she'll go into much more depth regarding some of those best practices and how a lot of those things work.
[00:11:41] So definitely refer to a workshop for more information Okay, so this is something that comes up with every Vuex discussion now that we have you three is composition API. And a lot of people are basically asking like do we even need Vuex? [COUGH] After all if you can compose your functions in a way that allow you to share reactive state, I mean there's nothing stopping you from creating basically your own globalstate.js and then just inject like basically like importing it everywhere.
[00:12:10] And this is this is true, which is one of the reasons why as far as like sharing function like sharing reactive state, there's nothing stopping you from using composition API and replacing Vuex as a whole. But whenever someone is sort of we're thinking of tooling, we have to remember that it's more than just like the the sheer mechanics of what is happening.
[00:12:32] So I think the best analogy here is with view router now that you understand about like dynamically switching out components with composite the component, the component component Man, that's an awful. That doesn't mean that immediately you would go out like view router is still useful because while the core mechanics of switching components out is still ultimate just a component, component component.
[00:12:56] Vuex provides a lot of things that help us debug things and that's well, I think we have to remember when it comes to the importance of the tools that we select and how it impacts our scale at impacts our apps at scale. And so while composition API is great for sharing things, as we probably saw it doesn't have the built in experience that Vuex provides in terms of debug ability.
[00:13:18] So as for those who have used like the view dev tools, this means that like, time traveling, watching state, seeing what payload it is, all of that, which especially if we think about global state, is actually a really critical thing. Because you can imagine that if something does break in your global state, having that level of tooling to be able to see and like inspect all the different events that lead up to something breaking is to me like that is worth it.
[00:13:43] That's basically like that is I mean that is our value as developers, right is that when things we build new things and then when things break, we can fix them and so on. In that regard, I would say that yes, composition API does help, like alleviate a lot of these sort of needs for like what originally Vuex was designed for.
[00:14:02] But going forward especially with Vuex five and things are rewritten like Vuex is going to be a lot of us will be using composition API, as its core, just as view router did for the component component. And so all the DX and time that the team spends on that like that is just that's like free developer experience enhancements for your team.
[00:14:21] And so I would say even though even when you start using the composition API, still be careful to like, I think the viewers conventions are still going to be very valuable to enterprise grade applications as time goes on.