Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

vuex- state returned as function or object literal

I ran into a problem the other day, and asked the great stack community for a solution.

The problem:

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.

The solution

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

New questions

  1. What is happening under the hood of the store when state is defined as an object literal versus a function returning an object literal?

  2. 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.

like image 319
Andrew Kim Avatar asked Mar 29 '18 13:03

Andrew Kim


1 Answers

tl;dr The reason for using a function is Module Reuse.


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.

like image 181
acdcjunior Avatar answered Oct 22 '22 21:10

acdcjunior