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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs