JAVASCRIPT
Mastering Reactivity with Vue 3 watch and watchEffect
Understand the differences and best use cases for Vue 3's `watch` and `watchEffect` to react to changes in reactive data efficiently and effectively.
// App.vue
<template>
<div>
<h1>Watch vs WatchEffect Example</h1>
<section>
<h2>Using `watch`</h2>
<input type="text" v-model="firstName" placeholder="First Name" />
<input type="text" v-model="lastName" placeholder="Last Name" />
<p>Full Name: {{ fullName }}</p>
<p>Watch Log: {{ watchLog }}</p>
</section>
<section style="margin-top: 30px;">
<h2>Using `watchEffect`</h2>
<input type="number" v-model="quantity" />
<input type="number" v-model="price" />
<p>Total: {{ total }}</p>
<p>WatchEffect Log: {{ watchEffectLog }}</p>
</section>
<section style="margin-top: 30px;">
<h2>Stopping Watchers</h2>
<button @click="stopAllWatchers">Stop all watchers</button>
</section>
</div>
</template>
<script setup>
import { ref, watch, watchEffect, computed, onMounted } from 'vue';
// --- Watch Example ---
const firstName = ref('John');
const lastName = ref('Doe');
const watchLog = ref('');
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
// Watch a single reactive source (ref or getter function)
const stopWatchFirstName = watch(firstName, (newValue, oldValue) => {
watchLog.value += `
FirstName changed from '${oldValue}' to '${newValue}'`;
console.log('Watch: firstName changed:', newValue);
});
// Watch multiple sources (array of refs/getters)
// The handler receives an array of new values and an array of old values
const stopWatchFullName = watch([firstName, lastName], ([newFName, newLName], [oldFName, oldLName]) => {
watchLog.value += `
FullName parts changed. New: ${newFName} ${newLName}, Old: ${oldFName} ${oldLName}`;
console.log('Watch: firstName or lastName changed:', newFName, newLName);
}, { immediate: true, deep: true }); // immediate: runs handler immediately on setup; deep: watches nested properties of objects
// --- WatchEffect Example ---
const quantity = ref(10);
const price = ref(5);
const watchEffectLog = ref('');
const total = computed(() => quantity.value * price.value);
// watchEffect automatically tracks its dependencies and re-runs when any of them change.
// It runs immediately on setup.
const stopWatchEffect = watchEffect(() => {
const currentTotal = quantity.value * price.value; // `quantity` and `price` are tracked as dependencies
watchEffectLog.value += `
Calculated total: ${currentTotal}`;
console.log('WatchEffect: quantity or price changed. Current total:', currentTotal);
// You can also access a `onCleanup` function to register cleanup callbacks
// onCleanup(() => {
// console.log('WatchEffect cleanup for total calculation.');
// });
});
// --- Stopping Watchers ---
const stopAllWatchers = () => {
stopWatchFirstName();
stopWatchFullName();
stopWatchEffect();
console.log('All watchers stopped.');
watchLog.value += '
-- ALL WATCHERS STOPPED --';
watchEffectLog.value += '
-- ALL WATCHERS STOPPED --';
};
// Watchers are automatically stopped when the component unmounts.
// Manually stopping is useful for specific scenarios.
onMounted(() => {
// For demonstration, we'll stop one watcher after 5 seconds
// setTimeout(() => {
// stopWatchFirstName();
// console.log('FirstName watcher stopped after 5 seconds.');
// }, 5000);
});
</script>
<style scoped>
section {
border: 1px solid #ccc;
padding: 20px;
margin-bottom: 20px;
border-radius: 8px;
background-color: #f9f9f9;
}
input {
margin-right: 10px;
padding: 5px;
border: 1px solid #ddd;
border-radius: 4px;
}
p {
white-space: pre-wrap;
}
</style>
How it works: Both `watch` and `watchEffect` are used in Vue 3 to reactively perform side effects when state changes, but they differ in how they determine dependencies. `watch` explicitly requires you to specify the reactive sources (refs, reactive objects, or getter functions) it should observe. It gives you access to both the new and old values. `watchEffect`, on the other hand, automatically tracks any reactive dependencies accessed during its initial run and re-runs whenever any of those dependencies change. `watchEffect` is simpler when you want to run a side effect based on a reactive state without needing to specify all dependencies upfront or access old values. Both return a function that can be called to stop the watcher, although they are automatically cleaned up when the component unmounts.