Search code examples
vue.jsvuejs2vuex

Do not mutate Vuex store state outside mutation handlers (ERROR)


So I'm getting the following error message:

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

The reason behind this error according to the Vuex docs is because the following code tries to directly mutate the state.

<template>
   <div v-for="item in basicInformation" :key="item.id">
     <el-col :span="12">
          <el-form-item label="First Name">
              <el-input v-model="item.firstName" />
          </el-form-item>
     </el-col>
   </div>
</template>

export default {
        computed: {
            ...mapState(['teststore']),
            basicInformation: {
                get() {
                    return this.teststore.personData.basicInformation
                },
                set(value) {
                    this.$store.commit('updateBasic', value)
                }
            }
        }
    }

I get why Vuex is throwing that error, since I am still trying to directly mutate the state. So a solution for this particular problem according to the docs would be using a two-way computed property with a setter (like in the example above), and that works fine for a single inputfield. However, I have A LOT of inputfields for the user to fill in. Consider the following code:

personData: {
        basicInformation: [
            {
                firstName: '',
                surname: '',
                email: '',
                phone: '',
                street: '',
                city: '',
                position: '',
                website: '',
            }
        ],
}

Creating a two-way computed property with a setter for all my inputfields seems very verbose. Is there an easy way where I can use Vuex and preferably v-model with an array of objects to create inputfields?


Solution

  • I would do something like this:

    Codesandbox: https://codesandbox.io/s/vuex-store-ibjbr

    <template>
      <div>
        <div v-for="(value, key) in basicInformation" :key="key">
          <el-col :span="12">
            <el-form-item :label="keyToLabel(key)">
              <el-input :value="value" @input="onInput({ key, value: $event })" />
            </el-form-item>
          </el-col>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      computed: {
        ...mapState(['teststore']),
        basicInformation() {
          return this.teststore.personData.basicInformation
        },
      },
      methods: {
        onInput({ key, value }) {
          this.$store.commit('updateBasic', { key, value })
        },
        keyToLabel(key) {
          // This is only an example
          const keyToLabel = {
            firstName: 'First Name',
            // And so on...
          }
          return keyToLabel[key]
        },
      },
    }
    
    // And in your store, something like this
    const store = {
      // ...
      mutations: {
        updateBasic(state, { key, value }) {
          state.teststore.basicInformation[key] = value
        },
      },
    }
    </script>