I ran into a problem the other day, and asked the great stack community for a solution.
I nested the same module in other modules but I was defining state in this way:
state: {
// some state here
}
What was happening is all of my modules despite seeming being nested under disparate modules all shared the same state.
state() {
return {
// state here instead
}
}
The solution is to have a function return state rather than define it as an object literal. The why somewhat makes sense. Here are my questions
What is happening under the hood of the store when state is defined as an object literal versus a function returning an object literal?
Why would you ever not use the function version? It seems like easily the default choice, but even in vuex docs for modules
, they opt to show state as an object literal.
What is happening under the hood of the store when state is defined as an object literal versus a function returning an object literal?
For that, better check under the hood:
var Store = function Store (options) {
// ...
var state = options.state; if ( state === void 0 ) state = {};
if (typeof state === 'function') {
state = state() || {};
}
The code above, as you can see, checks if a state
was provided. If not, it assigns an empty object ({}
) as initial state
.
Next it checks if state
was a function
. If it was, it executes it and assigns to state
what it returned. If it returned undefined
(or any falsy value) it, again, assigns to state
the empty object {}
.
So that's the difference between providing state
as an object or function: if one was provided, it is executed. If an object is provided, it is assigned directly.
Why would you ever not use the function version? It seems like easily the default choice, but even in vuex docs for modules, they opt to show state as an object literal.
In general, yes, the object version may be more common, because you typically only declare the store
object (and its state
) once and just use it in your Vue instance.
A use case for the state
function, otoh, is Module Reuse:
Module Reuse
Sometimes we may need to create multiple instances of a module, for example:
- Creating multiple stores that use the same module (e.g. To avoid stateful singletons in the SSR when the runInNewContext option is false or 'once');
- Register the same module multiple times in the same store.
Another possible case would be if you declared a Vuex module just once and attempted to use it more than one time under different namespaces.
As the examples above are similar, here's a demo (of the module case) to illustrate the problem:
const personModule = {
namespaced: true,
state: {name: "name"},
mutations: {
changeName(state, data) { state.name = data }
}
}
const myStore = new Vuex.Store({
strict: true,
modules: {
aliceNamespace: personModule,
bobNamespcace: personModule
}
});
new Vue({
store: myStore,
el: '#app',
mounted() {
this.changeAlicesName("Alice");
this.changeBobsName("Bob");
},
computed: {
...Vuex.mapState('aliceNamespace', {alicesName: 'name'}),
...Vuex.mapState('bobNamespcace', {bobsName: 'name'})
},
methods: {
...Vuex.mapMutations('aliceNamespace', {changeAlicesName: 'changeName'}),
...Vuex.mapMutations('bobNamespcace', {changeBobsName: 'changeName'})
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<p>Alice's name: {{ alicesName }}</p>
<hr>
<p>Bob's name: {{ bobsName }}</p>
<hr>
<button @click="changeAlicesName('Eve')">Change Alice's Name</button>
</div>
As you can see, when we use the state, the same object is assigned as state
of both modules. The effect of this is when we edit a module, the other is affected. In reality, they may be two different modules, but their state
is just one same object.
In the example below, on the other hand, when we declare state
as a function, we can reuse the module declaration freely, as many times as we want:
const personModule = {
namespaced: true,
state() { // changed to a function
return {name: "name"} // changed to a function
}, // changed to a function
mutations: {
changeName(state, data) { state.name = data }
}
}
const myStore = new Vuex.Store({
strict: true,
modules: {
aliceNamespace: personModule,
bobNamespcace: personModule
}
});
new Vue({
store: myStore,
el: '#app',
mounted() {
this.changeAlicesName("Alice");
this.changeBobsName("Bob");
},
computed: {
...Vuex.mapState('aliceNamespace', {alicesName: 'name'}),
...Vuex.mapState('bobNamespcace', {bobsName: 'name'})
},
methods: {
...Vuex.mapMutations('aliceNamespace', {changeAlicesName: 'changeName'}),
...Vuex.mapMutations('bobNamespcace', {changeBobsName: 'changeName'})
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<p>Alice's name: {{ alicesName }}</p>
<hr>
<p>Bob's name: {{ bobsName }}</p>
<hr>
<button @click="changeAlicesName('Eve')">Change Alice's Name</button>
</div>
Because state
is a function, it will generate a different state
instance for each module, working as expected all the way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With