V-bind not updating class on store state change

Viewed 3518

I am pretty new to Vue and Nuxt. I am trying to get my head around $stores.

I created a state object and gave it a property which is a simple boolean. I'd like to add a class to an element depending on whether or not that property is true. Here's how I created the store:

const store = () => {
    return new Vuex.Store({
        state: {
            foo: "You got the global state!",
            userSidebarVisible: true
        },
    })
}

In my vue file I have the following:

<template>
    <div>
        <div>Hello!</div>
        <button v-on:click="showSidebar">Click</button>
        <div v-bind:class="{active: userSidebarVisible}">the sidebar</div>
        <div>{{$store.state.userSidebarVisible}}</div>
    </div>
</template>



<script>
    export default {
        data: function() {
            return {
                userSidebarVisible: this.$store.state.userSidebarVisible,
            }
        },
        methods: {
            showSidebar: function() {
                if (this.$store.state.userSidebarVisible === true) {
                    this.$store.state.userSidebarVisible = false;
                } else {
                    this.$store.state.userSidebarVisible = true;
                }
            }
        }
    }
</script>

When I click the button, the active class doesn't toggle, but the text within the last <div> does get updated. I am wondering what I am doing wrong here. Doing the same thing with local data property seems to work as intended.

1 Answers

First of all, you should not change the $store state outside of a mutation.

You need to add a mutation method to your store for updating userSidebarVisible:

state: {
  userSidebarVisible: true
},
mutations: {
  SET_USER_SIDEBAR_VISIBLE(state, value) {
    state.userSidebarVisible = value;
  }
}

Secondly, if you want your Vue instance's data to reflect the state data, you can make userSidebarVisible a computed property with getter and setter functions:

computed: {
  userSidebarVisible: {
    get() {
      return this.$store.state.userSidebarVisible;
    },
    set(value) {
      this.$store.commit('SET_USER_SIDEBAR_VISIBLE', value);
    }
  }
}

Here's an example:

const store = new Vuex.Store({
  state: {
    userSidebarVisible: true
  },
  mutations: {
    SET_USER_SIDEBAR_VISIBLE(state, value) {
      state.userSidebarVisible = value;
    }
  }
})

new Vue({
  el: '#app',
  store,
  computed: {
    userSidebarVisible: {
      get() {
        return this.$store.state.userSidebarVisible;
      },
      set(value) {
        this.$store.commit('SET_USER_SIDEBAR_VISIBLE', value);
      }
    }
  },
  methods: {
    toggleSidebar() {
      this.userSidebarVisible = !this.userSidebarVisible;
    }
  }
})
.active {
  color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.4.0/vuex.min.js"></script>
<div id="app">
  <button v-on:click="toggleSidebar">Click</button>
  <div v-bind:class="{active: userSidebarVisible}">the sidebar</div>
  <div>Global state: {{$store.state.userSidebarVisible}}</div>
  <div>Vue instance state: {{userSidebarVisible}}</div>  
</div>

Related