In the Mutations chapter, you can see an example that uses mapMutations to map mutations to component methods.
It also says that
You can commit mutations in components with this.$store.commit('xxx')
So the question comes, is committing a mutation directly from a component (not through an action) fine in vuex?
If it is, then this diagram may need to update:
Should I keep using the mapMutations method?
As far as I understood, actions are mainly used for asynchronous operations which might comprise multiple mutations. So yes, it's ok to directly use mutations, that's what they are made for. Only when you have more complex workflows involving multiple mutations you should wrap them in an action.
MapMuation is just a helper that lets you use your defined mutations (in mutation-types) as methods of your component. It is exactly the same with $store.commit but with an easier notation.
I don't know in which context your diagram is but mutations can be called from everywhere (components, services, actions, getters etc...) en will do its work finely by synchronously updating the store.
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.
I've always seen Redux Reducers very similar to the mapEventToState function of BLoC State Management. However now I notice a big difference between the two patterns and it is that the Reducers must be "pure functions" while the function mapEventToState (in many examples) can even make http request.
At this point I wonder if it is actually possible to manage mapEventToState as "pure function" adding maybe another layer of abstraction.
As I see, mapEventToState from Bloc is a merge of Reducer and Middleware layers from Redux.
In Redux, actions can be intercepted by middlewares that creates new actions (side effects) parsed by reducers. IMHO this is one of the reasons Bloc is less cumbersome and more readable than Redux.
A good idea is to extract the data layer and inject the implementation on your bloc, receiving results as futures and yielding the new states.
In the Vuex guide it is suggested to use constants for Mutation types. I was thinking about doing this for Actions and Getters too but then I realized the guide never mentions using constants for Actions and Getters.
But why not? The advantages to using constants for Actions and Getters are pretty much the same as for Mutations. So why is this not recommended explicitly? Is there something that I am missing about why you would use them for Mutations, but not Actions and Getters?
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.
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.