While porting an existing app from Vue2 to Vue3 I faced a surprising problem.
How can I make Vue3 watch an "external" array for changes?
This worked perfectly fine in Vue2, but stopped working in Vue3:
<ul id="list">
<li v-for="t in dataArray"> {{t}} </li>
</ul>
<script>
var numbers = [1,2,3]; //this is my external array
var app = Vue.createApp({
data() { return { dataArray : numbers } } //bind Vue to my external array
}).mount("#list");
numbers.push(4); //UI not updating, but worked fine in Vue2
</script>
I know I can call app.dataArray.push
instead, or call $forceUpdate
, etc. but is there a way to force Vue to simply monitor an existing array?
I guess the broader question is: how to bind Vue3 to an arbitrary plain-JS object? The object can be too complex to rewrite or can come from an external API that I don't control. This is trivial in Vue2 or Angular (two-way binding with any plain object, whether or not it's part of the instance/component)
P.S. This looks like a huge breaking change in Vue3 that is not mentioned anywhere at all.
UPDATE:
According to a @Dimava's answer it looks looks like the least painful way of fixing the above code is this:
var numbers = [1,2,3]; //my external array that came from API
numbers = Vue.shallowReactive(numbers); //convert to a reactive proxy
You need to make your array Reactive
¹
import { reactive, ref } from 'vue'
const numbers = [1,2,3];
const reactiveNumbers = reactive(numbers)
reactiveNumbers.push(4)
// or, if you will need to reassign the whole array
const numbersRef = ref(numbers)
numbersRef.value.push(4)
numbersRef.value = [3, 2, 1]
// or, in the old style, if you are old
const data = reactive({
numbers: [1, 2, 3]
})
data.numbers.push(4)
data.numbers = [3, 2, 1]
¹ (or ShallowReactive
if it contains a lot of big objects that shouldn't be reactive for performance reasons)