简体   繁体   中英

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?

You can create a copy of basicInformation to have this inside component scope.

So associate a computed property to basicInformation and use it in v-models should kill this warning.

<template>
   <div v-for="item in localBasicInformation" :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>

<script>
computed: {
  localBasicInformation() {
    return [...this.basicInformation];
  },
},
</script>

Just pay attemption about the differences between shallow and deep copy. If this.localBasicInformation is deep, spread operator will not work and you will need something like cloneDeep from lodash.

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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM