2

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 Answers 4

1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Using a computed-setter is perfectly valid. Computed are getter only by default but they can be getter/setter when you need them to (i.e: when used as v-model in an input).
Ahh, good call. I'm not sure-Vuex-fluent so I didn't know that pattern. Thanks for the correction!
1

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.

2 Comments

I also tried this option, but it appeared the same warning
What I posted here should work. You should create a minimal reproducible example, which would allow us to test any potential solutions and figure out all aspects. I'll only be able to look further into this in about 10-12 hours, if you haven't found an answer by then.
0

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.

5 Comments

According to docs, the first difference between mutations and actions is that "Instead of mutating the state, actions commit mutations." and second is that they can contain async operations.
But it dont tells you to use mutations only in actions. It tells you that you should change state only through Vuex mutations in the actions. Main immutable rule is that you can't change state directly. Vuex mutations is sync and actions is async. And if you don't need async, better way to use mutations.
You can't use observables with v-model. You need to call a next() on the observable, which is what the action does. The advantage of getter/setter is that you don't need two names in the component and you also don't need to specify the method in @change. I believe it's much cleaner.
You can just read link mutations chapter. At the bottom of the page they have described actions/mutations difference and why actions was added.
Getter/setter has advantages. But here author should use it on lower level.
0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.