4
votes

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?

1
Here's a related question that didn't seem to share the same underlying issue: stackoverflow.com/questions/42294030/…pandyserkis
The code here looks perfectly fine. However, I don't understand what is going wrong. it seems to be happening in some other component? what is updated with what?dube
Thank you @dube! I just put a summary in before I noticed your comment. The very shortest answer is that the state is changing based on whatever value is in the component's inputs. Only "Commit All" within the devtools seems to "lock" the state.pandyserkis

1 Answers

6
votes

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 });
}