40

I have a Vuex store, which I'm injecting into my instance:

import store from '../store';
    
const mainNav = new Vue({
  el: '#main-nav',
  store,
  components: { NavComponent }
});

And I'm creating a computed property from that store in the component:

computed: {
  isWide() {
    return this.$store.state.nav.type === 'wide';
  }
}

This does create the this.isWide property for the template on component initialization, but when the store value is updated, the component doesn't register this - the old value is still on the template.

What am I doing wrong here?

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
babbaggeii
  • 7,577
  • 20
  • 64
  • 118

8 Answers8

57

Your code should work if you setup everything correctly: http://codepen.io/CodinCat/pen/RKZeZe?editors=1010

A very common mistake is that you didn't give your state initial data.

You should declare the state shape explicitly, so instead of

state: {}

// or

state: {
  nav: {}
}

do this:

state: {
  nav: {
    type: '...'
  },
  ...
}

or the properties will not be reactive, like this example: http://codepen.io/CodinCat/pen/ggxBEV?editors=1010

And make sure you did update the state, maybe the problem is just you didn't update the state correctly as you expected.

CodinCat
  • 15,530
  • 5
  • 49
  • 60
  • Brilliant - this was the correct answer. I presume this is the case for all objects, not just Vuex? – babbaggeii Jan 25 '17 at 17:04
  • 1
    Same in the components' data. Without giving the initial state, Vue cannot convert it to be reactive – CodinCat Jan 26 '17 at 02:42
  • "And make sure you did update the state, maybe the problem is just you didn't update the state correctly as you expected." - this is the ticket – James Westgate Oct 25 '18 at 11:44
  • 6
    "A very common mistake is that you didn't give your state initial data" -> this. – Jivan Apr 10 '19 at 11:43
15

Actually, you need to use Vue.set() function to make the new property reactive.

changeType () {
   Vue.set(this.$store.state.nav, 'type', 'wide');
}

See this codepen: https://codepen.io/DedicatedManager/pen/JZgpaa

I did a whole screen capture video on this subject: youtu.be/8GHczab2Lbs

10

When adding new properties to an Object from your Vuex store, you should either use

Vue.set(obj, 'newProp', 123)

or replace that Object with a fresh one

state.obj = { ...state.obj, newProp: 123 }

From Vuex docs

ssten
  • 1,848
  • 1
  • 16
  • 28
  • How will you do it if you have multi-level array? like `something[0].inside[0].value`? – user3779015 Jan 12 '20 at 05:00
  • I think the best way is to make a tmp copy of the multi-level array and do you're changes. Then just overwrite the whole thing `something` – ssten Jan 13 '20 at 06:43
2

you should not manipulate store objects directly from component methods, you should commit mutations on store object. Mutations are functions that have listeners and when called changed variables are tracked and changes are propagated. If you need to do something async like submit data to the server you have to go even one step further define actions on the store, do async call and then commit mutations from there to update state object. I know this sounds so annoying but once you use to it it is pretty straight forward.

const store = new Vuex.Store({
  state: {
    nav: {
      type: 'not wide by default'
    }
  },
  mutations: {
    changeType: (state, type) => {
      state.nav.type = type;
    }
  }
})

Vue.component('NavComponent', {
  template: '<div>isWide?: {{isWide}}</div>',
  computed: {
    isWide () {
      return this.$store.state.nav.type === 'wide'
    }
  }
})

new Vue({
  el: '#app',
  store,
  methods: {
    changeType (type) {
      this.$store.commit('changeType', type);
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.1.0/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.js"></script>
<div id="app">
  <nav-component></nav-component>
  <button @click="changeType('wide')">
    Change to Wide
  </button>
  <button @click="changeType('narrow')">
    Change to Narrow
  </button>
</div>

https://codepen.io/zoransa/pen/KyqvWd?editors=1010

it also works directly at stat.nav without type https://codepen.io/zoransa/pen/xPrLyW?editors=1010

Zoran
  • 2,653
  • 2
  • 13
  • 9
  • it works on codepen here I obviously have not set it up OK – Zoran Nov 13 '17 at 22:11
  • Tbh I don’t use store mutations in my vue apps as i usually have too many things to change in too many different ways from too many places within the app, it would be a pain in the backside having to write a function for each case. – Nick M Jul 09 '18 at 00:40
  • Hi Nick well you do not have to put everything in the store only data that you use on multiple places and let components have own data and access to to store trough computed properties and for updates you need to send them trough mutations anyway – Zoran Jul 09 '18 at 19:19
  • Seems I can change data in store from within components as well (without mutations) – Nick M Jul 09 '18 at 20:57
  • yes some people actually do not use full vuex but do something like... the most simple data object on window level let state = { user: 'John', age: 38}; then in vue data just put data() { state: state, something: 'whatever'} – Zoran Jul 13 '18 at 02:54
0

This was my problem.

const store = new Vuex.Store({
  state: {
    isTrackPlaying: false,
  },
  ...
});

I forgot to set the initial state and now it is reactive.

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
chovy
  • 72,281
  • 52
  • 227
  • 295
0

This is how I solved the problem:

I have state objects which contain other objects. I tried using Vue.set whenever there is a mutation, but it was messy and still didn't work. So I created a boolean state variable called toggleState, and provided a getter for it. Whenever a mutation changes a main state object, it also does state.stateToggle =! state.stateToggle. Anyone who cares can watch for changes to stateToggle, and do whatever is needed. In my case, it's re-building a treeview with the mutated main objects.

dippas
  • 58,591
  • 15
  • 114
  • 126
0

In my case, i just forgot to pass the variable 'state' in my mutation, and because of this, it was not reactive

const mutations = {
  [mutationTypes.storeTaskStart](state) {
    state.isSubmitting = true;
  },
};
SardarDev
  • 11
  • 1
-1

Both are Reactive

$vm.$set

this.$set(some_object, 'key', 'value')

Vue.set

Vue.set(some_object, 'key', 'value')
Balaji
  • 9,657
  • 5
  • 47
  • 47