28
votes

Why do I get this error:

Error [vuex] Do not mutate vuex store state outside mutation handlers.

What does it mean?

It happens when I try to type in the edit input file.

pages/todos/index.vue

<template>
  <ul>
    <li v-for="todo in todos">
      <input type="checkbox" :checked="todo.done" v-on:change="toggle(todo)">
      <span :class="{ done: todo.done }">{{ todo.text }}</span>
      <button class="destroy" v-on:click="remove(todo)">delete</button>

      <input class="edit" type="text" v-model="todo.text" v-todo-focus="todo == editedTodo" @blur="doneEdit(todo)" @keyup.enter="doneEdit(todo)" @keyup.esc="cancelEdit(todo)">

    </li>
    <li><input placeholder="What needs to be done?" autofocus v-model="todo" v-on:keyup.enter="add"></li>
  </ul>
</template>

<script>
import { mapMutations } from 'vuex'

export default {
  data () {
    return {
      todo: '',
      editedTodo: null
    }
  },
  head () {
    return {
      title: this.$route.params.slug || 'all',
      titleTemplate: 'Nuxt TodoMVC : %s todos'
    }
  },
  fetch ({ store }) {
    store.commit('todos/add', 'Hello World')
  },
  computed: {
    todos () {
      // console.log(this)
      return this.$store.state.todos.list
    }
  },
  methods: {
    add (e) {

      var value = this.todo && this.todo.trim()
      if (value) {
        this.$store.commit('todos/add', value)
        this.todo = ''
      }

    },
    toggle (todo) {
      this.$store.commit('todos/toggle', todo)
    },
    remove (todo) {
      this.$store.commit('todos/remove', todo)
    },

    doneEdit (todo) {
      this.editedTodo = null
      todo.text = todo.text.trim()
      if (!todo.text) {
        this.$store.commit('todos/remove', todo)
      }
    },
    cancelEdit (todo) {
      this.editedTodo = null
      todo.text = this.beforeEditCache
    },
  },
  directives: {
    'todo-focus' (el, binding) {
      if (binding.value) {
        el.focus()
      }
    }
  },
}
</script>

<style>
.done {
  text-decoration: line-through;
}
</style>

stores/todos.js

export const state = () => ({
  list: []
})

export const mutations = {
  add (state, text) {
    state.list.push({
      text: text,
      done: false
    })
  },
  remove (state, todo) {
    state.list.splice(state.list.indexOf(todo), 1)
  },
  toggle (state, todo) {
    todo.done = !todo.done
  }
}

Any ideas how I can fix this?

4
You can just turn off Vuex strict mode.Xhua

4 Answers

30
votes

It could be a bit tricky to use v-model on a piece of state that belongs to Vuex.

and you have used v-model on todo.text here:

<input class="edit" type="text" v-model="todo.text" v-todo-focus="todo == editedTodo" @blur="doneEdit(todo)" @keyup.enter="doneEdit(todo)" @keyup.esc="cancelEdit(todo)">

use :value to read value and v-on:input or v-on:change to execute a method that perform the mutation inside an explicit Vuex mutation handler

This issue is handled here: https://vuex.vuejs.org/en/forms.html

9
votes

Hello I have get the same problem and solve it with clone my object using one of the following:

{ ...obj} //spread syntax 
Object.assign({}, obj)
JSON.parse(JSON.stringify(obj))

For your code I think you need to replace this part

  computed: {
    todos () {
      // console.log(this)
      return this.$store.state.todos.list
    }
  }

With this

  computed: {
    todos () {
      // console.log(this)
      return {...this.$store.state.todos.list}
    }
  }

I don't make sure if this is the best way but hope this helpful for other people that have the same issue.

4
votes

There is no headache if you can use lodash

computed: {
...mapState({
      todo: (state) => _.cloneDeep(state.todo)
    })
}
0
votes
export default new Vuex.Store({
    ...
    strict: true
})

try to comment "strict"