SyntaxStudy
Sign Up
Vue.js Watchers: watch vs watchEffect
Vue.js Beginner 1 min read

Watchers: watch vs watchEffect

`watch` lets you observe specific reactive sources and run a callback when they change. It is lazy by default — the callback does not run on initial render — and you receive both the new and the old value as arguments, which is useful for comparing or cleaning up after the previous value. You can watch a `ref`, a `reactive` property via a getter function, or an array of sources simultaneously. `watchEffect` automatically tracks every reactive dependency accessed inside its callback. It runs immediately on creation (unlike the lazy default of `watch`) and re-runs whenever any dependency changes. It's perfect for synchronisation tasks such as keeping a third-party widget in sync with reactive state, where you don't care about old values. Both `watch` and `watchEffect` return a stop handle you can call to tear down the watcher early. They also accept an `onCleanup` argument for running cleanup logic — cancelling a fetch, clearing a timer — before the next invocation or when the component unmounts. Use `{ immediate: true }` with `watch` when you need the eager behaviour of `watchEffect` but still want access to previous values.
Example
<script setup>
import { ref, watch, watchEffect } from 'vue';

const userId  = ref(1);
const profile = ref(null);

// ── watch: lazy, receives old + new ──────────
watch(userId, async (newId, oldId, onCleanup) => {
  const controller = new AbortController();
  onCleanup(() => controller.abort());   // cancel prev fetch

  const res    = await fetch(`/api/users/${newId}`, { signal: controller.signal });
  profile.value = await res.json();
  console.log(`Changed from ${oldId} → ${newId}`);
});

// ── watch multiple sources ────────────────────
const page  = ref(1);
const query = ref('');
watch([page, query], ([newPage, newQuery]) => {
  console.log('Fetch page', newPage, 'query', newQuery);
});

// ── watchEffect: eager, auto-tracks ──────────
const log = ref([]);
watchEffect(() => {
  log.value.push(`userId=${userId.value} page=${page.value}`);
});

// ── Immediate watch ───────────────────────────
watch(userId, (id) => console.log('immediate, id=', id), { immediate: true });
</script>