Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Watchers: watch and watchEffect

A watcher is a function that Vue calls when certain data changes. computed returns a new value, while a watcher performs an action: saves data, sends a request, writes to the console. Such actions are called side effects.

The watch function

The first argument is what to track, the second is what to do on a change:

<!-- src/App.vue -->
<script setup>
import { ref, watch } from 'vue'
 
const query = ref('')
 
watch(query, (newValue, oldValue) => {
  console.log(`Search: was "${oldValue}", now "${newValue}"`)
})
</script>
 
<template>
  <input v-model="query" placeholder="Find a task">
</template>

Type "br" into the field — the browser console prints two lines: first was "", now "b", then was "b", now "br". The v-model directive links the field to the variable; it is covered in detail in the forms lesson.

Saving tasks

A typical job for watch is saving the list to localStorage after every change:

<script setup>
import { ref, watch } from 'vue'
 
const saved = localStorage.getItem('todos')
const todos = ref(saved ? JSON.parse(saved) : [])
 
watch(todos, (value) => {
  localStorage.setItem('todos', JSON.stringify(value))
}, { deep: true })
</script>

After a page reload the tasks are still there. The deep: true option makes the watcher fire when fields inside tasks change, such as done, not only when the whole array is replaced.

Options

OptionWhat it does
deep: trueTracks changes of nested fields
immediate: trueCalls the function right away on creation, not only on change
once: trueFires only once

Tracking part of an object

To watch a single field, pass a getter function that returns it:

<script setup>
import { ref, watch } from 'vue'
 
const filter = ref({ query: '', onlyActive: false })
 
watch(() => filter.value.onlyActive, (value) => {
  console.log('Only active:', value)
})
</script>

The watcher fires when onlyActive is toggled and ignores changes of query.

The watchEffect function

watchEffect finds dependencies on its own: it runs the function right away and remembers which reactive data it read.

<script setup>
import { ref, watchEffect } from 'vue'
 
const todos = ref([])
 
watchEffect(() => {
  document.title = `Tasks: ${todos.value.length}`
})
</script>

The browser tab title becomes "Tasks: 0" at once and changes every time a task is added or removed.

watchwatchEffect
SourceGiven explicitlyFound automatically
First runOn changeImmediately
Old valueAvailableNot available

Common mistake

Passing a value to watch instead of a source:

<script setup>
watch(filter.value.query, (value) => { /* ... */ })
</script>

filter.value.query is just a string at the moment of the call; Vue prints a warning and does not track it. Pass the ref itself or a function: watch(() => filter.value.query, ...).

Thanks for reading!

Donate to the site — we work hard for you.