Tutorials Logic, IN info@tutorialslogic.com

Vue Watchers watch watchEffect

When to Use Watchers

Watchers let you run side effects in response to reactive data changes - things that computed properties can't do: async operations, DOM manipulation, calling external APIs, or logging.

Feature watch() watchEffect()
Source declaration Explicit - you specify what to watch Automatic - tracks all accessed refs
Runs immediately No (unless immediate: true) Yes - runs on creation
Old value access Yes - (newVal, oldVal) No
Best for Specific data changes, need old value Multiple deps, immediate execution

watch and watchEffect - All Patterns

watch and watchEffect - All Patterns
<template>
  <div>
    <input v-model="query" placeholder="Search..." />
    <input v-model.number="userId" type="number" placeholder="User ID" />
    <p>{{ status }}</p>
  </div>
</template>

<script setup>
import { ref, reactive, watch, watchEffect, onUnmounted } from 'vue'

const query  = ref('')
const userId = ref(1)
const status = ref('Ready')
const user   = reactive({ name: '', email: '' })

// 1. Watch a single ref
watch(query, (newVal, oldVal) => {
  console.log(`Query changed: "${oldVal}" -> "${newVal}"`)
  status.value = `Searching for: ${newVal}`
})

// 2. Watch with options
watch(userId, async (newId) => {
  status.value = 'Loading...'
  const res = await fetch(`/api/users/${newId}`)
  const data = await res.json()
  user.name  = data.name
  user.email = data.email
  status.value = 'Loaded'
}, {
  immediate: true,  // run immediately on mount
  flush: 'post',    // run after DOM updates
})

// 3. Watch multiple sources
watch([query, userId], ([newQuery, newId], [oldQuery, oldId]) => {
  console.log('Either changed:', newQuery, newId)
})

// 4. Watch reactive object - need getter or deep: true
const form = reactive({ name: '', email: '' })

// Watch specific property with getter
watch(() => form.name, (newName) => {
  console.log('Name changed:', newName)
})

// Watch entire reactive object (deep)
watch(form, (newForm) => {
  console.log('Form changed:', newForm)
}, { deep: true })

// 5. watchEffect - auto-tracks dependencies
const stop = watchEffect(() => {
  // Automatically tracks query.value and userId.value
  document.title = `${query.value} | User ${userId.value}`
  console.log('Effect ran')
})

// 6. watchEffect with cleanup
watchEffect((onCleanup) => {
  const timer = setTimeout(() => {
    console.log('Debounced:', query.value)
  }, 500)

  onCleanup(() => clearTimeout(timer))  // cleanup before next run
})

// 7. Stop a watcher manually
onUnmounted(() => stop())  // stop watchEffect when component unmounts
</script>

When to Use Watchers - HTML Example

When to Use Watchers - HTML Example
<template>
  <div>
    <input v-model="searchQuery" placeholder="Search users..." />
    <p v-if="loading">Searching...</p>
    <ul v-else>
      <li v-for="user in results" :key="user.id">{{ user.name }}</li>
      <li v-if="results.length === 0 && searchQuery">No results</li>
    </ul>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const searchQuery = ref('')
const results = ref([])
const loading = ref(false)

// Debounced search with watch + cleanup
watch(searchQuery, (newQuery, _, onCleanup) => {
  if (!newQuery.trim()) {
    results.value = []
    return
  }

  loading.value = true

  // AbortController to cancel previous request
  const controller = new AbortController()

  const timer = setTimeout(async () => {
    try {
      const res = await fetch(`/api/users?q=${newQuery}`, {
        signal: controller.signal
      })
      results.value = await res.json()
    } catch (err) {
      if (err.name !== 'AbortError') console.error(err)
    } finally {
      loading.value = false
    }
  }, 400)  // 400ms debounce

  // Cleanup: cancel request and clear timer if query changes
  onCleanup(() => {
    clearTimeout(timer)
    controller.abort()
    loading.value = false
  })
})
</script>

Choose an Explicit Watch Source

watch accepts refs, reactive objects, getters, or arrays of sources. Pass a getter when observing one property of a reactive object. A deep watcher traverses nested state and can be expensive, so prefer a focused source or immutable replacement when possible.

Flush Timing Controls DOM Visibility

The default watcher runs before the component DOM update. Use post timing when the callback must inspect updated DOM, and use sync only for a narrow case that cannot tolerate batching. Select timing from the effect requirement instead of adding nextTick calls blindly.

Invalidate Stale Asynchronous Work

A watched search term can change again before its request finishes. Register cleanup that aborts or invalidates the older operation, then allow only the latest result to update state. Test rapid changes, rejection, unmount, and an empty source value so stale data cannot reappear.

Before you move on

Vue Watchers watch watchEffect Mastery Check

4 checks
  • Choose computed for derived state, watch for an explicit source, and watchEffect for automatically tracked effects.
  • Pass refs or getters correctly and avoid deep watching a large object without evidence it is needed.
  • Select pre, post, or sync flush timing from the DOM and scheduling behavior the effect requires.
  • Cancel stale asynchronous work during watcher cleanup so older results cannot overwrite newer state.

Vue JS Questions Learners Ask

watchEffect automatically tracks every reactive value read during its execution. That is convenient, but it can capture extra dependencies without you noticing. watch is more explicit because you name the source being observed.

When a watched value starts an async request, an older request can finish after a newer one and overwrite the latest state. Use watcher cleanup to cancel or invalidate the previous request.

Use flush: post when the watcher needs the DOM after Vue has applied the latest reactive update. Measuring height, focusing a newly rendered element, or reading rendered text can be wrong if the watcher runs before the DOM update.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.