Day 2 of 5
⏱ ~60 minutes
Vue.js in 5 Days — Day 2

Reactivity: ref, reactive, computed, and watchers

Vue's reactivity system is what makes your UI update automatically when data changes. This lesson explains how it works, when to use ref vs reactive, and how to derive state with computed properties and respond to changes with watchers.

How Vue's Reactivity Works

Vue tracks which reactive variables your template uses. When any of them change, Vue re-renders just the parts of the DOM that depend on that data. You don't call setState() or manually trigger updates — Vue handles it automatically.

There are two ways to create reactive state: ref() and reactive(). They behave differently and are suited to different situations.

ref vs reactive: When to Use Each

Use ref() for single values — strings, numbers, booleans, or even objects when you want to replace the whole thing. Use reactive() for objects and arrays when you'll only ever mutate their properties, never replace the whole object.

Vue Script
import { ref, reactive } from 'vue'

// ref: good for primitives and single values
const count = ref(0)
const username = ref('alice')
const isLoggedIn = ref(false)

// Access with .value in 

watch and watchEffect: Side Effects

Sometimes you need to run code in response to data changes — like calling an API when a search term updates, or saving to localStorage when a setting changes. That's what watch and watchEffect are for.

Vue Script
import { ref, watch, watchEffect } from 'vue'

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

// watch: explicit source, fires when source changes
// Gets old and new values
watch(searchQuery, async (newVal, oldVal) => {
  if (newVal.length > 2) {
    results.value = await fetchResults(newVal)
  }
})

// watch with options
watch(searchQuery, handler, {
  immediate: true,  // run once on mount
  deep: true        // watch nested object changes
})

// watchEffect: auto-tracks dependencies
// Runs immediately and whenever any reactive value it reads changes
watchEffect(() => {
  console.log('Search changed to:', searchQuery.value)
  // Vue automatically knows this depends on searchQuery
})

// Cleanup: stop watching when component unmounts
const stop = watchEffect(() => { /* ... */ })
// stop() when you're done
ℹ️
Use computed for derived values (things you display). Use watch for side effects (API calls, saving data, triggering animations). If you find yourself computing a value inside a watch, that's a signal to use computed instead.
📝 Day 2 Exercise
Build a Live Search Component
  1. Create a component with a text input bound to a ref search query.
  2. Add a hardcoded array of 20+ items (names, products, whatever you like).
  3. Use computed to filter the array based on the search query.
  4. Display the filtered results count with another computed property.
  5. Use watch to log every search query change to the console with a debounce of 300ms.
  6. Add a computed property for whether there are zero results, and show a 'No results found' message in that case.

Day 2 Summary

  • ref() is for single values. Access with .value in scripts, auto-unwrapped in templates.
  • reactive() is for objects. Access properties directly. Never replace the whole reactive object.
  • computed() caches derived values. Only recalculates when dependencies change.
  • watch() runs side effects when specific data changes. Gets old and new values.
  • watchEffect() auto-tracks its reactive dependencies and runs immediately.
Finished this lesson?