Course outline

Vue / Basic: reactive components and input contracts / Lesson 4

Computed values and deliberate watchers

Separate derived state from side effects and choose reactive sources that express the operation’s true dependencies.

About 5 min readContent reviewed
On this page

Lesson goals

  • Use computed for pure derived values.
  • Explain watch sources, timing, and cleanup boundaries.

Derive a value instead of synchronizing copies

A computed value describes a result derived from reactive inputs. Vue caches the result according to tracked dependencies and reevaluates when those dependencies require it. This is different from a method called during every render. Keep getters pure: do not send requests, mutate source arrays, or write another ref while calculating a display value. A derived filtered list generally needs no watcher that copies it into a second list. One authoritative source plus a clear derivation reduces contradictory state and makes the relationship easier to review and test.

Make a display relationship explicit

The example derives an unfinished count from a list owned by the component. Reading the computed value in the template unwraps its ref-like interface. Changing finished through the reactive list can change the count without a manual recount operation. The getter returns a number and does not mutate the list. If the source later comes from props or a store, retain the same ownership distinction: consumers derive views rather than silently editing someone else’s collection. Expensive derivation should be measured before adding more caches, because a cache can conceal stale-input bugs when keyed incorrectly.

vue A pure computed count
<script setup>
import { ref, computed } from "vue"
const notes = ref([
  { id: "n1", finished: false },
  { id: "n2", finished: true }
])
const unfinishedCount = computed(() =>
  notes.value.filter(note => !note.finished).length
)
</script>

<template>
  <p>Unfinished: {{ unfinishedCount }}</p>
</template>

The stated initial data yields Unfinished: 1 in a configured Vue SFC project. Marking n1 finished through the reactive owner yields zero. This expected framework behavior was checked against documentation, not executed in a browser.

Watch when a change should trigger an effect

watch observes an explicit source and runs a callback when that source changes under its options. A ref, reactive object, getter, or supported source array has different semantics from passing an already-read primitive. watchEffect discovers reactive reads made during synchronous execution; an async callback does not automatically track reads after its first await. Choose explicit watch when dependency identity matters. Watchers are appropriate for effects such as reacting to selection changes or coordinating a request, while computed is appropriate for a current derived value. Keeping that distinction prevents loops and unnecessary duplicate state.

Record a local transition from one selected id

The synthetic watcher appends selection transitions to a local history. That history is an event record, not a value derivable from only the current id, so an effect is a meaningful choice here. The source is selectedId itself, not selectedId.value. A default watcher is lazy and batched; several synchronous writes can be observed as one transition under normal scheduling. This is not a durable audit log and should not be used for security or billing delivery guarantees. Real external work also needs cancellation, error handling, and an appropriate owner.

javascript A local watcher over an explicit ref source
import { ref, watch } from "vue"

const selectedId = ref("n1")
const transitions = ref([])
watch(selectedId, (current, previous) => {
  transitions.value.push({ previous, current })
})

selectedId.value = "n2"

Use inside synchronous component setup or a deliberately managed effect scope. After Vue processes this single change, the history contains the n1-to-n2 transition. The fragment requires Vue and is not a standalone executed program.

Own watcher lifetime and update timing

Watchers created synchronously during component setup are normally tied to that component and stopped when it unmounts. A watcher created later in an asynchronous callback may need explicit management. Cleanup should invalidate obsolete effects before the next run or owner disposal. Vue 3.5 adds onWatcherCleanup with a synchronous registration requirement; the callback-provided cleanup interface remains another documented option. Use post-flush timing for effects that need the component’s updated DOM, and avoid synchronous watchers on frequently mutated collections without a measured reason. Deep watching has traversal cost and does not create independent old snapshots of nested objects.

Common errors and fixes

A computed getter changes source state

Why it happens
Derivation and side effects are mixed.
How to fix it
Keep the getter pure and move effects to explicit handlers or watchers.
Prevent it next time
Review computed functions for writes and external work.

A watcher receives a number instead of a source

Why it happens
The ref value was read before being passed.
How to fix it
Pass the ref or a getter that reads the property.
Prevent it next time
Name the watched source in the function contract.

An effect continues after its owner disappears

Why it happens
The watcher was created outside the expected managed lifetime.
How to fix it
Manage the stop handle or effect scope deliberately.
Prevent it next time
Review asynchronous watcher creation and cleanup.

Check your understanding

1. A label is always the uppercase version of title. Should a watcher copy it into another ref?

Show worked solution

A computed value normally expresses that direct relationship more clearly. A watcher creates another mutable value that can disagree with the source. Keep a watcher only if there is a separate effect or history requirement, and document that reason.

2. watchEffect reads a ref only after awaiting a promise. What dependency concern exists?

Show worked solution

Automatic dependency collection covers the synchronous execution segment. Use an explicit watch source or move the tracked read before await when that matches the intended effect. Then verify invalidation and cancellation rather than assuming all later reads are tracked.

Key takeaways

  • Computed values describe pure current relationships.
  • Watchers coordinate effects with explicit source and lifetime.
  • Timing and cleanup are part of the effect contract.

Official references