Reputation: 919
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://v2.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://v2.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)
})
},
Upvotes: 3
Views: 9004
Reputation: 20845
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
}
}
This works for ojects as well.
The performance hit is exaggerated for the real life use cases till you probably have hundreds (nested?) properties (objects) in your cloning object.
Either with clone
or cloneDeep
the clone takes 1/10 of a millisecond as reported with window.performance.now()
. So measured under Node with process.hrtime()
it could show even lower figures.
Upvotes: 10
Reputation: 22393
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 } }
]
}
}
Upvotes: -1