How to structurally compare the previous and new value of nested objects that are being used in `watch`, in Options API?

Viewed 205

I have a question which is a mix of both composition API and options API

What I want to do: I want to watch an object. That object is deeply nested with all kinds of data types.

  • Whenever any of the nested properties inside change, I want the watch to be triggered.
    • (This can be done using the deep: true option).
  • AND I want to be able to see the previous value and current value of the object.
    • (this doesn't seem to be possible because Vue stores the references of the objects, so, now the value and prevValue point to the same thing.)

In Vue3 docs, for the watch API, it says this

However, watching a reactive object or array will always return a reference to the 
current value of that object for both the current and previous value of the state. 
To fully watch deeply nested objects and arrays, a deep copy of values may be required.
 This can be achieved with a utility such as lodash.cloneDeep

And this following example is given

import _ from 'lodash'

const state = reactive({
  id: 1,
  attributes: {
    name: ''
  }
})

watch(
  () => _.cloneDeep(state),
  (state, prevState) => {
    console.log(state.attributes.name, prevState.attributes.name)
  }
)

state.attributes.name = 'Alex' // Logs: "Alex" ""

Link to docs here - https://v3.vuejs.org/guide/reactivity-computed-watchers.html#watching-reactive-objects

However, this is composition API (if I'm not wrong). How do I use this way of using cloneDeep in a watch defined in options API?

As an example, this is my code

watch: {
    items: {
      handler(value, prevValue) {
        // check if value and prevValue are STRUCTURALLY EQUAL
        let isEqual = this.checkIfStructurallyEqual(value, prevValue)
        if (isEqual) return
        else this.doSomething()
      },
      deep: true,
    },
}

I'm using Vue 3 with Options API.

How would I go about doing this in Options API? Any help would be appreciated! If there's another way of doing this then please do let me know!

1 Answers

I also asked this question on the Vue forums and it was answered.

We can use the same syntax as provided in the docs in Options API using this.$watch()

data() {
  id: 1,
  attributes: {
    name: ''
  }
}

this.$watch(
  () => _.cloneDeep(this.attributes),
  (state, prevState) => {
    console.log(state.name, prevState.name)
  }
)

this.attributes.name = 'Alex' // Logs: "Alex" ""
Related