Xar
Xar

Reputation: 7910

Vue and Vuex: computed property isn't called when changing state

I'm quite new with Vue and Vuex so please bear with me.

I want to make the computed function versions() get called when I change state.template, but I'm failing to do so. More specifically, when state.template.versions changes.

This is part of the component that I want to re-render when state.template.versions changes. You can also see the computed property versions() which I want to be called:

    <el-dropdown-menu class="el-dropdown-menu--wide"
      slot="dropdown">
      <div v-for="version in versions"
        :key="version.id">
           ...
      </div>
    </el-dropdown-menu>
    ...
    computed: {
      ...mapState('documents', ['template', 'activeVersion']),
      ...mapGetters('documents', ['documentVersions', 'documentVersionById', 'documentFirstVersion']),
    versions () {
       return this.documentVersions.map(function (version) {
          const v = {
            id: version.id,
            name: 'Draft Version',
            effectiveDate: '',
            status: 'Draft version',
          }
          return v
        })
    },

This is the getter:

  documentVersions (state) {
    return state.template ? state.template.versions : []
  },

This is the action:

  createProductionVersion (context, data) {
    return new Promise((resolve, reject) => {
      documentsService.createProductionVersion(data).then(result => {
        context.state.template.versions.push(data)  // <-- Here I'm changing state.template. I would expect versions() to be called
        context.commit('template', context.state.template)

        resolve(result)
      })

This is the mutation:

  template (state, template) {
    state.template = template
  },

I've read that there are some cases in which Vue doesn't detect chanegs made to an array, but .push() seems to be detected. Source: https://v2.vuejs.org/v2/guide/list.html#Caveats

Any idea on why the computed property is not being called when I update context.state.template.versions?

Upvotes: 2

Views: 1855

Answers (3)

Seblor
Seblor

Reputation: 7136

The issue may come from state.template = template. You guessed correctly that it was a reactivity issue, but not from the Array reactivity, but the template object.

Vue cannot detect property addition or deletion. This includes affecting a complex object to a property. For that, you need to use Vue.set.

So your mutation should be :

template (state, template) {
  Vue.set(state, "template", template)
},

https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats

Upvotes: 3

Ahmed Jaouadi
Ahmed Jaouadi

Reputation: 223

Your function won't get called because this is wrong:

context.state.template.versions.push(data) 
context.commit('template', context.state.template)

the context.state object just points to your current state nothing more.

My suggested solution will be:

  1. First You need to declare your store state correctly

    state: {
       template: {
          versions: []
       }
    }
    
  2. You need to update your getter to look like this with no unnecessary conditioning:

    documentVersions: state => return state.template.versions,

  3. add a new mutation

     ADD_VERSION: (state, version) => {
       state.template = {
         ...state.template,
         versions: [...state.template.versions, version]
        };
      }
    
  4. your action should like this now:

       createProductionVersion({commit}, data) {
           return new Promise((resolve, reject) => {
             documentsService.createProductionVersion(data).then(result => {
               commit('ADD_VERSION', data);
               resolve(result);
             });
           });
         }
    
  5. In your component I suggest to change your computed property from a function to an object that contains a get and set methods (set is optional)

    versions: {
      get() {
        return this.documentVersions.map(function (version) {
          const v = {
            id: version.id,
            name: 'Draft Version',
            effectiveDate: '',
            status: 'Draft version',
          }
          return v
        })
      }
    },
    

Upvotes: 1

s4k1b
s4k1b

Reputation: 3085

I think this error occurred because you did not declare your store state correctly. Make sure you have the versions property in your template object.

state: {
  template: {
    versions: []
  }
}

This way, any changes in the versions property will be detected by vue.

Upvotes: 1

Related Questions