Darkstarone
Darkstarone

Reputation: 4730

Vuex store only keeps one component updated, not both

So I have a Vuex store with a few variables, it looks something like this:

var dataStore = new Vuex.Store({
    state: {
        consumer_id: undefined,
        some_other_data: 0
    },
    mutations: {
        update: function(type, payload) {
            state = Object.assign(type, payload);
        }
    }
});

And I have two components, both of which are top level Vue components. However MultiSim surrounds Sim. They both use the following method to access consumer_id:

consumer_id: {
    get: function () {
        return this.$store.state.consumer_id;
    },
    set: function (value) {
        this.$store.commit('update', {consumer_id: value});
    }
},

The issue is that while changes to consumer_id are propagated to Sim, MultiSim only ever sees undefined (the default value). MultiSim changes consumer_id, and this is captured correctly by all three components, but if Sim changes, MultiSim does not update.

For example:

Can anyone see where I might have gone wrong? Is there a way to force an update to all components?

Edit:

So I've found a solution, but it's more of a hack than anything:

Essentially, the computed method of MultiSim is not updating, even though it can access the actual stored value just fine.

Upvotes: 0

Views: 399

Answers (1)

Bert
Bert

Reputation: 82489

Assuming all of your properties on state are initialized when you create the store, you could send a generic payload to your update mutation using an approach like this:

const store = new Vuex.Store({
  state: {
    consumer_id: undefined,
    some_other_data: 0
  },
  mutations: {
    update: function(state, payload) {
      for ([key, value] of Object.entries(payload))
        state[key] = value
    }
  }
})

This will allow your update mutation to be generic and set the properties on state.

Here is an example.

console.clear()

const store = new Vuex.Store({
  state: {
    consumer_id: undefined,
    some_other_data: 0
  },
  mutations: {
    update: function(state, payload) {
      for ([key, value] of Object.entries(payload))
        state[key] = value
    }
  }
})

const Sim = {
  template: `
<div>
Sim <input v-model="consumer_id"> {{consumer_id}}
</div>
`,
  computed:{
    consumer_id: {
      get: function () {
        return this.$store.state.consumer_id;
      },
      set: function (value) {
        this.$store.commit('update', {consumer_id: value});
      }
    },    
  }
}

const MultiSim = {
  template: `
    <div>
      MultiSim <input v-model="consumer_id"> {{consumer_id}}
    </div>
    `,
  computed:{
    consumer_id: {
      get: function () {
        return this.$store.state.consumer_id;
      },
      set: function (value) {
        this.$store.commit('update', {consumer_id: value});
      }
    },    
  }
}

new Vue({
  el: "#app",
  store,
  components: {Sim, MultiSim}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  <sim></sim>
  <multi-sim></multi-sim>
</div>

If the properties are not initialized on the state when the store is created, then you will need to use Vue.set.

const store = new Vuex.Store({
  state: {
  },
  mutations: {
    update: function(state, payload) {
      for ([key, value] of Object.entries(payload))
        Vue.set(state, key, value)
    }
  }
})

Also, both of these presume that you are creating the payload in every case like you do in your computed set.

Note: Object.entries is not supported in IE, so you may need to resort to Object.keys instead.

Upvotes: 1

Related Questions