Vue whining about store state mutation while not even using the store

Hey guys!

So I have the following error:
[vuex] do not mutate vuex store state outside mutation handlers.

Im aware that this is not allowed and the occasions when it occurs I use Object.assiggn to clone the property and bypass this.

But now where on a whole other level, let me explain.

I have a component called DotooTicketTypes with a prop called templateTickets. I use this component in my page and call it like this:

            <dotoo-ticket-types-edit
              ref="ticketTypesEdit"
              :template-tickets="template.ticket_types"
              :initial-vat-type="template ? template.vat_type : null"
              @vat-type-change="vatTypeChanged"
              @tickets-changed="ticketsChanged"
            />

Now in the component on created() I call this.loadTemplateTickets() that loads the ticket data in the component. The tickets data is loaded in two local properties called defaultTicket and customTickets which are declared in data(). as follows:

  data() {
    return {
      initialDefaultTicket: null,
      initialCustomTicket: null,
      defaultTicket: {
        name: null,
        quantity: null,
        price: null,
        order: 0,
      },
      customTickets: [],
      ticketsAreLoading: true,
      selectedVatType: null,
    }
  },

Now in loadTemplateTickets() I call setTicketsFromResponse(ticketTypes) (for reusability) and in there some stuff happens like converting the ticket price from cents to euros and adding the vat amount etc. The following is the full function:

    setTicketsFromResponse(ticketTypes) {
      console.dir(ticketTypes)
      if (ticketTypes) {
        const namelessTickets = ticketTypes.filter((ticket) => ticket.name === null)
        if (namelessTickets && namelessTickets.length > 0) {
          this.defaultTicket = namelessTickets[0]
          this.initialDefaultTicket = Object.assign({}, this.defaultTicket)
          if (this.defaultTicket.price) {
            // We need to show the price inc vat and in euros, not cents, so we use the helper
            this.defaultTicket.price = this.priceIncVat(this.defaultTicket.price, this.initialVatType, false)
            this.initialDefaultTicket.price = this.priceIncVat(this.initialDefaultTicket.price, this.initialVatType, false)
          }
        }

        const customTickets = ticketTypes.filter((ticket) => ticket.name !== null)
        if (customTickets && customTickets.length > 0) {
          customTickets.map((ticket) => {
            if (ticket.price) {
              // We need to show the price inc vat and in euros, not cents, so we use the helper
              ticket.price = this.priceIncVat(ticket.price, this.initialVatType, false)
            }
          })
          this.customTickets = customTickets
        } else {
          this.customTickets = []
        }
      }
    },

Now the part where I reassign this.defaultTicket.price triggers the [vuex] do not mutate vuex store state outside mutation handlers. error, I just can’t understand why!? Do any of you have any clue?

Here’s the full error:

client.js?06a0:84 Error: [vuex] do not mutate vuex store state outside mutation handlers.
    at assert (vuex.esm.js?2f62:94)
    at Vue.store._vm.$watch.deep (vuex.esm.js?2f62:834)
    at Watcher.run (vue.runtime.esm.js?2b0e:4568)
    at Watcher.update (vue.runtime.esm.js?2b0e:4542)
    at Dep.notify (vue.runtime.esm.js?2b0e:730)
    at Object.reactiveSetter [as price] (vue.runtime.esm.js?2b0e:1055)
    at VueComponent.setTicketsFromResponse (DotooTicketTypesEdit.vue?be5f:306)
    at VueComponent.loadTemplateTickets (DotooTicketTypesEdit.vue?be5f:254)
    at VueComponent.created (DotooTicketTypesEdit.vue?be5f:188)
    at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)

DotooTicketTypesEdit line 306 is the this.defaultTicket.price = this.priceIncVat(this.defaultTicket.price, this.initialVatType, false) line.

The error line next in the call stack is saying that price is a reactive setter, meaning by changing this you are changing the value within Vuex, thus the warning.

It’s likely coming from the top few lines where you filter and then assign this.defaultTicket = namelessTickets[0]. You aren’t creating a new object here, but using the referenced object so once you change the ticket price you mutate the original object.

1 Like

Thanks @JamesThomson ! I now changed:

this.defaultTicket = namelessTickets[0]

to:

this.defaultTicket = Object.assign({}, namelessTickets[0])

and that works. But I dont understand it. In the following function:

    loadTemplateTickets() {
      if (this.templateTickets) {
        this.setTicketsFromResponse(Object.assign([], this.templateTickets))
      }
    },

I already create a new object you see? So why is that not enough?

I would like to understand why (-:

And the error has moved to the following line:
ticket.price = this.priceIncVat(ticket.price, this.initialVatType, false) (one of the last lines in the setTicketsFromResponse function. I tried the following (which is what I now have)

    setTicketsFromResponse(ticketTypes) {
      console.dir(ticketTypes)
      if (ticketTypes) {
        const namelessTickets = ticketTypes.filter((ticket) => ticket.name === null)
        if (namelessTickets && namelessTickets.length > 0) {
          this.defaultTicket = Object.assign({}, namelessTickets[0])
          this.initialDefaultTicket = this.defaultTicket
          if (this.defaultTicket.price) {
            // We need to show the price inc vat and in euros, not cents, so we use the helper
            this.defaultTicket.price = this.priceIncVat(this.defaultTicket.price, this.initialVatType, false)
            this.initialDefaultTicket.price = this.priceIncVat(this.initialDefaultTicket.price, this.initialVatType, false)
          }
        }

        const reactiveCustomTickets = ticketTypes.filter((ticket) => ticket.name !== null)
        const customTickets = Object.assign([], reactiveCustomTickets)
        if (customTickets && customTickets.length > 0) {
          customTickets.map((ticket) => {
            if (ticket.price) {
              // We need to show the price inc vat and in euros, not cents, so we use the helper
              ticket.price = this.priceIncVat(ticket.price, this.initialVatType, false)
            }
          })
          // this.customTickets = customTickets
        } else {
          this.customTickets = []
        }
      }
    },

where I create a new array for customTickets but is does. not work, still the same error.

Thanks!