In Vue.js, a watcher is a function that observes a reactive data and executes every time that data changes.
Unlike computed properties, which return a calculated value, watchers are used to perform side effects (like making API calls, updating the DOM, or executing complex logic).
That is, watchers are like reactive listener functions, which allow us to observe changes in a property or data and execute an action when changes are detected.
For this, we have the watch and watchEffect functions that allow us to observe changes in one or several reactive properties and execute a function when the properties change.
Creating watchers with watch
The main function for creating watchers in Vue.Js is the watch function, which has the following basic syntax,
watch(source, callback, options);
- source: The reactive property we want to observe. It can be a
ref, areactive, acomputed, or even a function that returns a value. - callback: The function that will be executed when the source changes. It receives two parameters: the new value and the old value.
- options (optional): A configuration object that allows customizing the behavior of
watch.
Let’s see it with an example. Suppose we have a reactive property count and we want to execute a function every time count changes:
import { ref, watch } from 'vue';
export default {
setup() {
const count = ref(0);
watch(count, (newValue, oldValue) => {
console.log(`The value of count has changed from ${oldValue} to ${newValue}`);
});
return {
count,
};
},
};
In this example, every time count changes, the new value and the old value will be printed to the console.
Observing multiple sources with watch
watch also allows us to observe multiple sources simultaneously. To do this, we pass an array of sources as the first argument.
import { ref, watch } from 'vue';
export default {
setup() {
const firstName = ref('Juan');
const lastName = ref('Pérez');
watch([firstName, lastName], ([newFirstName, newLastName], [oldFirstName, oldLastName]) => {
console.log(`Full name changed from ${oldFirstName} ${oldLastName} to ${newFirstName} ${newLastName}`);
});
return {
firstName,
lastName,
};
},
};
In this case, the callback will be executed every time firstName or lastName changes.
watch options
The watch function accepts an optional third parameter that allows configuring its behavior. Some of the most common options are:
- immediate: Executes the
callbackimmediately after creating thewatch, even if the source hasn’t changed. - deep: Observes deep changes in nested objects or arrays.
By default, a watcher only executes when the observed value changes. However, if you want the watcher to execute immediately at the moment of creation, you can enable the immediate option.
Suppose we have a property message and we want it to be printed immediately when the component loads, in addition to reacting to any subsequent change.
<template>
<div>
<p>{{ message }}</p>
<button @click="changeMessage">Change message</button>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
const message = ref('Hello, world!');
// Watcher to observe changes in the message
watch(message, (newValue, oldValue) => {
console.log('The message changed:', newValue);
}, { immediate: true });
function changeMessage() {
message.value = 'New message!';
}
</script>
In this example, the watcher with the option immediate: true executes immediately at the start, showing the initial value of the message. Furthermore, it continues observing any subsequent change in the value of message.
By default, watchers only observe changes in the primitive value of a reactive reference. However, if the observed data is an object or an array, you can enable deep observation to detect changes in its internal properties.
Suppose we have an object user and we want to react to changes in its properties.
<template>
<div>
<p>Name: {{ user.name }}</p>
<p>Age: {{ user.age }}</p>
<button @click="changeName">Change name</button>
<button @click="changeAge">Change age</button>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
const user = ref({
name: 'Juan',
age: 30,
});
// Watcher to observe changes in the user object
watch(user, (newValue, oldValue) => {
console.log('The user changed:', newValue);
}, { deep: true });
function changeName() {
user.value.name = 'Carlos';
}
function changeAge() {
user.value.age++;
}
</script>
In this example, the watcher observes changes in the user object. Since the object is complex, we enable deep observation (deep: true) to detect changes in its internal properties.
Creating watchers with watchEffect
The other alternative for creating watchers is the watchEffect function. It is very similar to the watch function, but with the difference that it automatically tracks all dependencies used inside its callback.
That is, we don’t need to explicitly specify which properties to observe. Let’s see a summary of their differences,
| Feature | watch | watchEffect |
|---|---|---|
| Dependencies | Explicitly specified | Automatically detected |
| Typical use | To observe specific changes | For side effects |
| Access to values | New and old value | Only the new value |
| Configuration | More configurable | Less configurable |
The syntax of watchEffect is as follows
watchEffect(callback);
- callback: The function that will be executed every time any of its reactive dependencies change.
Let’s see it with an example,
import { ref, watchEffect } from 'vue';
export default {
setup() {
const count = ref(0);
watchEffect(() => {
console.log(`The value of count is ${count.value}`);
});
return {
count,
};
},
};
In this example, watchEffect automatically tracks count and executes the callback every time count changes.
