watch / watchEffect
watch runs a callback when a reactive source changes. Reach for it for side effects (logging, navigation, API calls). For derived values use computed.
watch, watchEffect, sources, options
EXAMPLE
<script setup>
import { ref, watch, watchEffect } from 'vue';
const id = ref(1);
const query = ref('');
const user = ref(null);
// 1) Watch a single source
watch(id, async (newId, oldId) => {
console.log(\`id changed from ${oldId} → ${newId}\`);
user.value = await api.getUser(newId);
});
// 2) Watch multiple sources
watch([id, query], ([nid, nq], [oid, oq]) => {
refilter(nid, nq);
});
// 3) Eager — fire immediately too (not just on change)
watch(id, fetchUser, { immediate: true });
// 4) Deep — react to nested mutations
const form = ref({ name: '', email: '' });
watch(form, (next) => save(next), { deep: true });
// 5) Cleanup — cancel in-flight work when deps change
watch(id, async (newId, _, onCleanup) => {
const ctrl = new AbortController();
onCleanup(() => ctrl.abort());
user.value = await fetch(\`/users/${newId}\`, { signal: ctrl.signal })
.then(r => r.json());
});
// 6) Getter source — derive what to watch
watch(
() => user.value?.role,
(role) => { if (role === 'admin') trackAdminLogin(); },
);
// 7) watchEffect — auto-tracks every reactive read in the function
watchEffect(() => {
if (id.value) save(\`${id.value}-${query.value}\`);
});
// 8) Stop a watcher manually
const stop = watch(id, fetchUser);
// later …
stop();
// 9) Flush timing
watch(id, callback, { flush: 'post' }); // after DOM updates
watch(id, callback, { flush: 'sync' }); // immediately (rare)
watch(id, callback, { flush: 'pre' }); // default — before render
</script>
Why it matters
watch needs an explicit source; watchEffect auto-tracks. Prefer watch when you need the old value, lazy execution, or precise dependencies; watchEffect for one-off “just run this when anything reactive changes”.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import { watch, watchEffect } from 'vue';
watch(count, (n, old) => console.log(n, old));
watchEffect(() => console.log(state.count));
Try it Yourself »
Discussion
Loading…