2
votes

I'm using Vuex and I've created a module called claims that looks like this:

import to from 'await-to-js'
import { functions } from '@/main'
import Vue from 'vue'

const GENERATE_TX_SUCCESS = 'GENERATE_TX_SUCCESS'
const GENERATE_TX_ERROR = 'GENERATE_TX_ERROR'

export default {
  state: [ ],
  mutations: {
    [GENERATE_TX_SUCCESS] (state, generateTxData) {
      state.push({ transaction: { ...generateTxData } })
    },
    [GENERATE_TX_ERROR] (state, generateTxError) {
      state.push({ transaction: { ...generateTxError } })
    }
  },
  actions: {
    async generateTx ({ commit }, data) {
      const [generateTxError, generateTxData] = await to(functions.httpsCallable('generateTx')(data))
      if (generateTxError) {
        commit(GENERATE_TX_ERROR, generateTxError)
      } else {
        commit(GENERATE_TX_SUCCESS, generateTxData)
      }
    }
  },
  getters: { }
}

Then, in the .vue component I do have this watch:

 watch: {
    claims: {
      handler (newTxData, oldTxData) {
        console.log(newTxData)
      }
    }
 }

The problem I'm facing here is that newTxData is the same as oldTxData.

From my understanding, since this is a push and it detects the change, it's not one of these caveats: https://vuejs.org/v2/guide/list.html#Caveats

So basically the problem is this one:

Note: when mutating (rather than replacing) an Object or an Array, the old value will be the same as new value because they reference the same Object/Array. Vue doesn’t keep a copy of the pre-mutate value.

https://vuejs.org/v2/api/#vm-watch

Then my question is: how should I workaround this in the mutation?

Edit:

I tried also with Vue.set(state, state.length, generateTxData) but got the same behaviour.

Edit 2 - temporary solution - (bad performance-wise):

I'm adapting @matthew (thanks to @Jacob Goh) to my solution with vuexfire:

computed: {
  ...mapState({
    claims: state => cloneDeep(state.claims)
  })
},
2

2 Answers

7
votes

This answer is based on this pretty smart answer by @matthew

You will need lodash cloneDeep function

Basically, create a computed value like this

computed: {
    claimsForWatcher() {
        return _.cloneDeep(this.claims);
    }
}

What happens is, everytime a new value is pushed into claims, claimsForWatcher will become an entirely new object.

Therefore, when you watch claimsForWatcher, you won't have the problem where 'the old value will be the same as new value because they reference the same Object/Array' anymore.

watch: {
    claimsForWatcher(oldValue, newValue) {
        // now oldValue and newValue will be 2 entirely different objects
    }
}

Warning: this comes with a performance cost as your data grows bigger

-1
votes

You can assign state to a new object:

  mutations: {
    [GENERATE_TX_SUCCESS] (state, generateTxData) {
      state = [
         ...state,
         { transaction: { ...generateTxData } }
      ]
    },
    [GENERATE_TX_ERROR] (state, generateTxError) {
      state = [
         ...state,
         { transaction: { ...generateTxError } }
      ]
    }
  }