Welcome to the third installment of our series on common Vue.js development pitfalls. In this part, we're going to unravel another intriguing challenge – "Watching Arrays."
Demystifying Array Watching in Vue 3
Watching arrays in Vue 3 can sometimes be tricky. Have you ever tried to watch an array, confident that your syntax is spot-on, only to find that the watcher doesn't trigger as expected until you add {deep: true}
? If you've experienced this, you're not alone. But rest assured, there's a reason behind this behavior, and it's time to shed some light on it.
Consider the seemingly straightforward code snippet below:
<script setup>
import { ref, watch } from "vue";
const numArr = ref([]);
const addRandom = () => {
numArr.value.push(Math.random());
};
watch(numArr, () => {
console.log("random number added");
});
</script>
<template>
<button @click="addRandom">Add a random number to the array</button>
</template>
You'd expect that when you click the button, a random number should be added to the numArr
array, and since we're watching the array, the console should log "random number added," right?
Well, here's where things get interesting: this won't work as expected 😧.
Yes, the random number is added to the array. You would then ask if the array is changing and it is being watched for changes, why the heck isn't it working 🤬🤬??
You can experiment with the code and witness the changes in behavior by using this CodeSandbox playground.
Now let's add the deep: true
option to the watcher and see what happens.
<script setup>
import { ref, watch } from "vue";
const numArr = ref([]);
const addRandom = () => {
numArr.value.push(Math.random());
};
watch(numArr, () => {
console.log("random number added");
},
{deep: true});
</script>
<template>
<button @click="addRandom">Add a random number to the array</button>
</template>
With this adjustment, you'd notice that the callback is now working as expected. But why does this make a difference? It's a valid question.
The key lies in understanding how Vue.js watches arrays.
When you replace an array entirely, you don't need to use the
deep
option. However, in scenarios where you're mutating the array, such as adding or removing items, or modifying existing elements, the deep option is essential.
It ensures that Vue.js watches for changes within the array's elements.
If we were to replace the entire array, we could achieve our goal without the deep
option. Here's an example of how you can modify our code to replace the array in its entirety:
<script setup>
import { ref, watch } from "vue";
const numArr = ref([]);
const addRandom = () => {
numArr.value = [...numArr, Math.random()]
};
watch(numArr, () => {
console.log("random number added");
});
</script>
<template>
<button @click="addRandom">Add a random number to the array</button>
</template>
While this approach is effective, some might find it less intuitive compared to simply using the push
method and enabling the deep
option. Additionally, what adds an extra layer of complexity is the fact that in Vue 2, you didn't need to use the deep
option for this purpose.
However, according to Vue 3 migration guides, this change was introduced as a breaking change. In Vue 3,
"when watching an array, the callback will only trigger when the array is replaced. If you need to trigger on mutation, the
deep
option must be specified.
"
So there you have it, if you find that your callbacks aren't firing as expected, the first thing to check is whether you've included the deep
option in your watcher. This simple addition can make a significant difference in how your Vue 3 application handles array watching.
Top comments (0)