2
votes

I get this error in my console when I try to change the text of an input text of my components:

[vuex] do not mutate vuex store state outside mutation handlers

I also use nuxt with vuex.

I have this in my store:

editor.js

export const state = () => ({
  project_blocks: [{
    id: null,
    type: 'text-right',
    content:{
      title: 'Title 2',
      text: 'Text 2',
    }
  },{
      id: null,
      type: 'text-left',
      content:{
        title: 'Title 1',
        text: 'Text 1',
      }
    }],
});

export const mutations = {
  SET_BLOCKS(state, blocks){
    state.project_blocks = blocks;
  },
};


export const getters = {
  project_blocks(state){
    return state.project_blocks;
  }
};

In my component I have:

Index.vue

<template>
   <component v-for="(block, index) in project_blocks" :key="'module'+index" :block="block" :is="block.type"></component>
</template>

<script>
   export default{
      computed: {
         project_blocks:{
           get(){
             return this.$store.getters['editor/project_blocks'];
           },
           set(val){
             this.$store.commit('editor/SET_BLOCKS', val);
           }
         },
       }
   }
</script>

And in my "component type" I have:

TextLeft.vue

<template>
   <input type="text" v-model="block.content.title">
   <input type="text" v-model="block.content.text">
</template>

<script>
   export default{
      props:['block']
   }
</script>

When I try to change the text of these input text I get this error: [vuex] do not mutate vuex store state outside mutation handlers and I don't know how to fix it :(

Thanks people!

4

4 Answers

1
votes

The error message is being (surprisingly?) helpful here. When you v-model something, you're saying "when this input changes, modify the value of this thing I'm giving you."

However, as the error says, this is causing you to directly mutate data meant to be stored by Vuex, which isn't how Vuex likes things done. You should be using your mutations.

Edit: the following comment is wrong. Thanks, tao! Side note: Your set(val) function shouldn't be in computed, it should be in methods.

1
votes

Vuex complains about v-model changing the state directly, because you're passing a getter (observable) to it.

To correctly update the state, you should use an action. Add an action performing that mutation and dispatch it inside the setter of your computed:

computed: {
  project_blocks:{
    get(){
      return this.$store.getters['editor/project_blocks'];
    },
    set(val){
      this.$store.dispatch('editor/setBlocks', val);
    }
  }
}

store/editor module:

actions: {
  setBlocks({ commit }, payload) {
    commit('SET_BLOCKS', payload);
  }
}

The mutation remains unchanged.


The example provided in forms only works because they're using a sub-property of obj. If they used obj itself (the top level getter value), they'd get the same error as you do.

0
votes

Main point of actions is async calls. You can use Vuex mutations, but not change state directly. This is right immutability definition (You can check Vuex doc). I think the main problem now is that your computed property returns vuex getters result and there are vuex watchers on items. And when you are changing it - error is shown.

0
votes

I fix the error! I create a data value inside my component and I change this value individually with two mutations.

TextLeft.vue:

<template>
   <input type="text" v-model="title">
   <input type="text" v-model="text">
</template>

<script>
   export default{
      props:['block'],
      data(){
         title: this.block.content.title,
         text: this.block.content.text
      },
      watch:{
          title(val){
             this.$store.commit('editor/SET_TITLE', { index: this.block.index, 
title: val });
      },
          text(val){
            this.$store.commit('editor/SET_TEXT', { index: this.block.index, text: val });
      }
    }
   }
</script>