Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

Watchers — watch and 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.

Featurewatch()watchEffect()
Source declarationExplicit — you specify what to watchAutomatic — tracks all accessed refs
Runs immediatelyNo (unless immediate: true)Yes — runs on creation
Old value accessYes — (newVal, oldVal)No
Best forSpecific data changes, need old valueMultiple deps, immediate execution
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>
<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>

Ready to Level Up Your Skills?

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