If i just want to return a member of my vuex state, should I define a getter for it? or is it o.k. to use mapState ?
My dilemma is as follows:
1) It seems redundent to have a getter that simply returns a member of the state as in:
getters: {
user (state) {
return state.user
}
}
when I can just use the following code in the consuming component and save the coding of the getter ?
computed: {
...mapState('auth',['user']),
}
However, if something did change in the future and I would like some computation done on the user object before it is returned then using a getter and
computed: {
...mapGetters('auth',['user']),
}
Would allow for a simpler change.
But if this is the recommended way to go then why provide mapState to begin with?
So should I use getters for this type of simple state memeber access? or not?
Technically, as you note, you can do either way. When there's no calculation involved, though, it becomes a matter of taste.
There's no authority in this, but I'd recommend as general advice:
If your application...
has a stablished state format AND
your state that doesn't tend to change AND
your application is not that big,
...then map to state.
Else, use getters.
In other words, if your application is somewhat small and should be stable, then, why not?
If else, other than the obvious "use getters when you need calculations", if your application:
tends to grow OR
has a state structure that is not stablished and tends to change OR
has deeply nested state.
Then use getters. They decouple you application from the state, allowing it to change more freely (which is frequently
needed "in times of trouble"), simplifying refactoring a lot.
In other words,
when in doubt, use getters instead of mapping directly to state.
Note: IMO, this question is the same as using Selectors in Redux. And, in Redux, the usage of selectors is widely recommended.
Related
In my Vuex code, I have countless trivial mutations, like:
setOption(state, payload) {
state.option = payload;
}
With hundreds of similar lines of code, it doesn't look to be good. Any better option?
Option 0 (New Recommended)
Consider switching to Pinia, new official state manager for Vue.
Mutations do not exist any more. These can be converted to actions instead, or you can just assign directly to the store within your components (eg. userStore.firstName = 'FirstName')
Option 1 (Old Recommended)
Keep it this way.
Pros:
Standard, well-documented.
IDE support. You can easily find usages of some mutation across your project.
Easy to add more logic when needed, e.g. age should be a positive number, etc...
Cons:
Might look repetitive, so it is easier to skip some non-trivial mutation. However, this can partly be solved by convention to put all trivial to the end of the file\section, as soon as mutation stops to be non-trivial - move it to the top of the file\section.
Option 2
Use a universal mutation :
mutate(state, payload) {
state[payload.property] = payload.with;
}
and call it:
commit('mutate', {
property: <propertyNameHere>,
with: <valueGoesHere>
});
Pros:
Clean mutation section, non-trivial mutations are visually distinct.
Cons:
A little harder to find related mutations, e.g. some can have two or more spaces after property:
More writing for each mutation call.
Not really compatible with a standard way, so you can end with non-trivial mutation with additional logic, but someone can miss it and use a unified mutation call.
Option 3
Use some code generator, e.g. Hygen
Pros:
You have the same code as from Option 1, including IDE support and future extension.
You can set it to add in all places by a single command line - to state, mutation, and action (if needed).
With typescript you also need to change interface of the store, wich can also be done with the same command line.
Cons:
Can be hard to set up.
Need additional informing of new team members.
Option 4
Use some Vuex plugin with predefined mutations, e.g. this one
Pros:
Minimal setup.
Clean mutation section, non-trivial mutations are visually distinct.
Cons:
Needs some knowledge about the plugin.
A little harder to find related mutations
Not really compatible with a standard way, so you can end with non-trivial mutation with additional logic, but someone can miss it and use a mutation call provided by a plugin.
So I know getters are primarily used to return state data that is manipulated in some way but is it best practice to create a getter if you just want to return the state value itself without mutating or anything?
I don't think it necessarily saves any code, if anything creates more, by creating a getter to return all the values I need.
Where I have the option, I personally always prefer getters (and C#-style properties) over direct memeber variable accesses be it internally from within the same class or externally from outside the class for two different reasons:
1- They are great when debugging “access points” (for example if you had to monitor who accesses a member variable and when then you just put a print or a breakpoint in the getter instead of doing loads of searching in your code)
2- If in the future you need to change the way a member variable is defined and/or used then getters give you a single point of focuse/change which can be changed to reflect changes in the definitions and meanings of the actual backing member variable
Note the same applies to setters too.
In C++ it is not that common practice but I don’t remember the last time I did not use getter/setters for something!
Hope this helps!
Getters are essentially good for derived states: https://vuex.vuejs.org/guide/getters.html
It's also a good practice to maintain your getters in the same place and use mapGetters in your reactive components.
What is the difference between:
this.currentLanguage = this.$store.getters.currentLanguage;
and this for getters:
this.currentLanguage = this.$store.state.currentLanguage;
Also this:
this.$store.commit('setLanguages', this.languages);
is gives me same results with this in mutations :
this.$store.state.languages = this.languages;
I'm receiving what I want in both cases. And getting value direct is easier because I don't need to write getter/mutation.
Why should I use getters/mutations?
you do not need to use getters in this case. getters are like computed properties on a regular Vue component, so you only need them when the data is a more complex descendant of the other data.
you want to use mutations, however, because even if it looks like the this.$store.state.languages = this.languages is working, it isn't necessarily updating it correctly. if you have other components depending on the store, it may not receive the new data because it hasn't undergone the full mutation process.
When you use mutations you have a clear record of what changed in things like vue-devtools. It also allows for time travel debugging.
This is really nice when you're debugging.
The state vs getters is a lot more of a gray area. I personally like not having to think if something is in state or a getter. If I need to get something from the store I use a getter.
That question got answered pretty well by the Vuex Docs itself:
Again, the reason we are committing a mutation instead of changing store.state.count directly, is because we want to explicitly track it. This simple convention makes your intention more explicit, so that you can reason about state changes in your app better when reading the code. In addition, this gives us the opportunity to implement tools that can log every mutation, take state snapshots, or even perform time travel debugging. https://vuex.vuejs.org/guide/ - nearly bottom
So basically the idea is to explicitly track changes. This allows other tools to attach themself to those mutations and actions and track your code which is a huge benefit for large scale applications. It will also make your code more readable and at a later stage much easier to update. Ever read into an old project where they always changed on the go without one "way" to do it - it won't happen here.
Is there anything Vuex can do that the following simple shared store can not? --
let store = {
user: {
name: 'John Doe'
}
};
new Vue({
el: '#app-one',
data: {
bar: 'foo',
shared: store
}
});
new Vue({
el: '#app-two',
data: {
foo: 'bar',
shared: store
}
});
If so, what are those common use cases?
Deep down, a Vuex store is just a Vue reactive object as the one you are adding.
So if you would only use the state of the Vuex store via direct access, then the two approaches work the same.
The thing is, if, in Vuex, you are using only the state directly, you are pretty much underusing the tool. You are paying the price, but not reaping the benefits.
Vuex has getters, which are like reusable computed properties. They also allow you to decouple your apps from the inner structure of the store's state.
Vuex has modules/namespaces, which allow you to break down your store code and better organize it as it grows.
Vuex also have actions and mutations. Actions allow you to create "methods" that are reusable by any Vue instance (or component) that uses the store. Another important thing is actions and mutations enforce a "protocol" that will also help you to better organize your code and, most importantly of all: will help you debug your code more easily when things get troublesome (and they will, if your app grows enough).
For one example, consider an app which many points of the app change the same bit of state. Consider also that some of these changes are done asynchronously. Using Vuex, you know that any async operation is being done within actions, so if you have any "race" problem, the source of the issue must be there. In Vuex, you also know that, ultimately, the changes to the state are made at mutations only (and synchronously). So if things get crazy, you can always place a breakpoint at that mutation and find out what the heck is invoking it. If you don't use Vuex, in any reasonably big app, the changes will come from too many places at too many rates, making your life not so easy after all.
My Vuex store contains objects with a variety of complexity. Some have nested objects, some have arrays of nest objects.
I could create generic function to mutate a specified property along the lines of:
setProperty(state,{ type, id, prop, value })
{
state[type][id][prop] = value;
}
but that will quickly get complicated for nested object, arrays of objects. It also seems very tedious to have to create a mutation for every single object property, nested or otherwise.
What are the best practices for creating mutations to modify objects, nested objects, arrays, etc?
Another related issue, is it considered bad form to pass the objects into the mutations as opposed to looking them up in the state:
setProperty(state,{ obj, prop, value })
{
obj[prop] = value;
}
Generally speaking, it is best if you just could avoid nested state structure at first place. I'm not sure how your data are structured, but if you're doing that because you have relationship between those object or area of object, it might be worth trying normalizing the state shape.
Here is the good article from Redux author. It's talking about Redux but the core concept is very same for the Vuex.
https://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
And the Vues ORM is a library that do that for you automatically.