首页 > 解决方案 > 错误:不要在突变处理程序之外改变 vuex 存储状态

问题描述

场景
我正在使用 Vuex,在其中存储一些数据,在我的情况下是工单详细信息。最初,我有一张有一系列折扣的票,它是空的。一旦我点击“添加折扣”按钮,我就会安装名为“testDiscount”的组件,该组件在已安装的挂钩中推动({"code": "Foo", "value":"Boo"})商店中特定票的折扣数组中的第一个对象。当我尝试在此组件中输入输入框(更改其状态)时出现问题,其中出现错误“不要在突变处理程序之外改变 Vuex 存储状态。”。我怎样才能最好地处理这个?

测试.vue

<template>
  <div>
    <test-component v-for="(t, key) in tickets" :key="key" :ticket-key="key" :tid="t.id"></test-component>
  </div>
</template>

<script>
import TestComponent from "~/components/testComponent.vue";
export default {
  layout: "noFooter",
  components: {
    "test-component": TestComponent,
  },

  data() {
    return {
      tickets: this.$store.state.ticketDiscount.tickets,
    };
  },

  mounted() {
    if (this.tickets.length == 0) {
      this.$store.commit("ticketDiscount/addTicket", {
        id:
          this.$store.state.ticketDiscount.tickets.length == 0
            ? 0
            : this.$store.state.ticketDiscount.tickets[
                this.$store.state.ticketDiscount.tickets.length - 1
              ].id + 1,
        discount: [],
      });
    }
  },
};
</script>

ticketDiscount.js

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

export const mutations = {
    addTicket(state, ticket) {
        state.tickets.push(ticket);
    },

    addDiscount(state, property) {
        state.tickets.find(ticket => ticket.id == property.id)[property.name].push(property.value);
    }

测试组件.vue

<template>
  <div>
    <h3>Ticket number: {{ticketKey + 1}}</h3>
    <button @click="showDiscount = true">Add discount</button>
    <test-discount v-model="discount_" v-if="showDiscount" :tid="tid"></test-discount>
  </div>
</template>

<script>
import testDiscount from "~/components/test-discount.vue";
export default {
  components: {
    testDiscount,
  },

  data() {
    return {
      showDiscount: false,
      tid_: this.tid,
    };
  },
  props: {
    tickets: Array,
    ticketKey: { type: Number },
    tid: { type: Number, default: 0 },
  },

  methods: {
    updateTicket() {
      this.$emit("updateTicket", {
        id: this.tid_,
        value: {
          discount: this.discount_,
        },
      });
    },
  },

  mounted() {
    this.$watch(
      this.$watch((vm) => (vm.discount_, Date.now()), this.updateTicket)
    );
  },

  computed: {
    discount_: {
      get() {
        return this.$store.state.ticketDiscount.tickets.find(
          (ticket) => ticket.id == this.tid
        )["discount"];
      },

      set(value) {
        // set discount
      },
    },
  },
};
</script>

testDiscount.vue

<template>
<div class="container">
        <div class="title">
            <img src="~/assets/svgs/price_tag.svg" />
            <span>Discount code</span>
            {{ discounts }}
        </div>
            
        <div class="discount-container">
            <div v-for="(c,idx) in discounts" class="discounts" :key="idx">
                <div class="perc-input">
                    <input style="max-width: 50px;" v-model.number="c.discount" type="number" min="1" max="100" step="1" placeholder="10">
                    <div>%</div>
                </div>
                <input class="code-input" v-model="c.code" placeholder="Code">
                <img src="~/assets/svgs/bin.svg" title="Delete code" @click="deleteCode(idx)" v-if="discounts.length > 1"/>
            </div>
        </div>

        <span @click="newDiscount" class="add-another">+ Add another discount</span>
    </div>        
</template>

<script>
export default {
    props: {
        value: {
            type: Array,
        },
        tid: { type: Number, default: 0 },
    },
    data() {
        return {
            discounts: this.value,
        }
    },
    mounted() {
        if (this.discounts.length == 0) {
            this.newDiscount();
        }
    },
    methods: {
        newDiscount() {
            this.$store.commit('ticketDiscount/addDiscount',
            {   
                "id": this.tid,
                "name": "discount",
                "value": { code: null,discount: null }
            });
        },
        deleteCode(index) {
            this.discounts.splice(index, 1);
        }
    },
    watch: {
        discounts() {
            this.$emit('input', this.discounts)
        }
    },
    beforeDestroy() {
        this.$emit('input', []);
    }
}
</script>

标签: vue.jsvuex

解决方案


你不应该v-model在这种情况下使用。

<input style="max-width: 50px;" v-model.number="c.discount" .../>

你可以设置值

<input style="max-width: 50px;" :value="c.discount" @change="handleValueChange" .../>

然后在handleValueChange函数中提交仅针对该值进行更新的操作。


推荐阅读