Search code examples
vuejs2vuexvue-devtools

Vue2 + Vuex Commit Not Committing (without Vue devtools)


I'm trying to accomplish what should be a basic task with Vuex, but for some reason it's not working, and after searching far and wide, I greatly appreciate any help.

What I'm trying to do:

Update a list (object) of objects in my store with a new property (object).

What's going wrong:

Before I even dispatch the action to commit the new object from my component (I'm accessing the action via mapActions), certain properties in any existing objects in the list are updated with the values tied to the inputs/v-models in my component. As my code shows below, I know reactivity with objects is an issue, so I'm using Vue.set(...) per the docs (Mutations Follow Vue's Reactivity Rules)

Why I don't think I'm doing something completely stupid...(but am probably wrong)

When I check on the mutation in DevTools, the mutation is logged as expected, and when I press "Commit"/"Commit All", the existing objects in my list no longer respond to changes in the inputs. This is obviously the behavior I expect to occur given that the action is literally supposed to commit the change to the state. Yet why does it not work within the code, and only within the devtools?

I apologize again for what is probably a basic issue, but I've seen a few others with a similar issue and no written explanation as to what we're missing...

Initial State

const state = {
  quotes: {}
}

Mutation

mutations: {
  [types.ADD_QUOTE] (state, payload) {
    Vue.set(state.quotes, payload.id, payload)
  }
}

Action

actions: {
  addQuote ({ commit }, payload) {
    commit(types.ADD_QUOTE, payload) 
  }    
}

Component

<template>
  <div class="quote-block">
    <label>price</label>
    <input type="text" v-model="quote.price">
    <label>id</label>
    <input type="text" v-model="quote.id">
    <!-- Just displaying props below -->
    <div>{{ quote.item }}</div>
    <div>{{ quote.vendor }}</div>
    <div>Qty: {{ quote.qty }}</div>
    <button @click="addQuote(quote)">Submit quote</button>
  </div>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  props: {
    vendor: String,
    item: String,
    qty: Number
  },
  data () {
    return {
      quote: {
       id: '',
       price: '',
       timestamp: Date.now(),
       vendor: this.vendor,
       item: this.item,
       qty: this.qty
      }
     }
    },
  methods: {
    ...mapActions([
      'addQuote'
    ])
   }
  }

To summarize, within the devtools, I see values for id and price changing within objects that I set to state.quotes - they're apparently tied to the v-models for quote.price and quote.id within my component. It's only when I "Commit All" within devtools that the properties for those objects stop changing. Why isn't the commit method within the action making these commits?


Solution

  • You fell into the object reference trap. The quote is an object, which is passed to the action as the payload. When you commit that payload, it saves the reference into your state.

    Now both the component and the store point to the same object.

    The solution is to copy the input into a new object, either with the spread operator or Object.assign

    Its generally a good practice to always copy the payload in mutators (if it is an object)

    function(state, payload){
      Vue.set(state.quotes, payload.id, {... payload });
    }