Subscribe on changes!

Ref.value is updated in array althought it shouldn't

avatar
Jul 26th 2022

Vue version

3

Link to minimal reproduction

https://sfc.vuejs.org/#eNplUb1ugzAQfpWrF4iUgNpuiETt1qVSlqpD3YEYk5DgH/nsdEC8e88BoqZh4vz93H13PXu1NjsHyQpWonCt9YDSB7vhulXWOA89ONksweh3E7SXNQzQOKMgIVXCNdfCaPRgnamD8AjryE+/vhf/kAno8RQKSB6fnqudSAZizTyF+4mTvMmuM/BpXFc/JH8owdaVl9urYbqA9QZ6rmHukp2rLsisarx024AHInkXJNdDdLmGSO+VOEktqdIbNxogzlnm44JoNVR4qWxHw1wqgDJuDOjr+9lvoJYE5CNS7oL3RsOL6FpxWnN2k4WzSf5xeZ1nGg1GJRHK/NqVLdl4n5WqbHZEo+mClzh8ApCzYgwY3+hYsebs4L3FIs+xEfHuR8yM2+f0lznaTKtkJlGtds78oHRkzFm0oCgDG34BsVTC4A==

Steps to reproduce

Click on button and you will see that product updated in array by itself.

Next, comment out push function in onMounted and uncomment push with shallow copy (take it from below as playground cut off any code comments). Upon clicking button product won't update by itself anymore

What is expected?

If we are pushing value to array the data shouldn't update by itself. If we call isRef(product.value) it will return false, so.... if value is not reactive why is it updating? Is pushing shallow copy a recommended way of handling such cases? Pushing unref(product.value) still causes unwanted behaviour.

What is actually happening?

Ref shouldn't update by itself.

Pasting onMounted with comments, because Vue SFC Playground cut them off

onMounted(() => {
  // if we push ref's value it will update itself upon button click
   products.value.push(product.value) 
  
  // if we push ({...product.value}) product wont update by itself
  // products.value.push({...product.value})
})
avatar
Jul 26th 2022

I'm a bit confused what you are having an issue with, exactly.

In JS, objects are passed as references. pushing an object to an array does not create a new object. So changing the object in product.value of course will affect the object in the array - because they are the same object. That's just JS.

I think you are kind of aware of this, as you show the "solution" yourself: make a copy of the object if you don't want one to affect the other.

So: Am I missing something?

avatar
Jul 26th 2022

Thanks @LinusBorg for quick response. I understand passing by reference. Therefore I have updated reproduction. Using plain JS won't cause update in jsProducts array. TLDR it won't cause template update, but array will still be updated. The lack of template update caused wrong assumptions.

import { ref, onMounted } from 'vue'

const products = ref([])
const product = ref({sku: '123abc'})

let jsProducts = []
let jsProduct = {sku: '123abc'}

const updateProduct = () => {
  product.value.afterPush = true
}

const updateJsProduct = () => {
  jsProduct.afterPush = true
}

onMounted(() => {
  products.value.push(product.value)
  jsProducts.push(jsProduct)
})
avatar
Jul 26th 2022

Manipulating a nonreactive array will not cause a re-render, so the ouput in the html does not change. That's also expected.

You can see that the plain array also changed when you change the reactive one - then the component updates the whole template and you see both arrays containing the new property.

Still all expected behavior. Still just passing an object reference.

avatar
Jul 26th 2022

Ok, now I see that array updated correctly, it was just the template that did not update (which was causing confusion and incorrect assumptions).