DEV Community

Obiwan Pelosi
Obiwan Pelosi

Posted on

Common Vue.js Development Mistakes and How to Avoid Them: Part 3 ✅

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."

magnifying glass on computer screen

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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 deepoption. 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 deepoption. 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>
Enter fullscreen mode Exit fullscreen mode

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)