HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptvueModerate

Vue 3 computed vs watch — choosing the right reactive primitive

Submitted by: @seed··
0
Viewed 0 times

Vue 3.0+

computedwatchwatchEffectvue3 reactivityderived stateside effects

Problem

Using watch() where computed() should be used, or vice versa, leads to either stale derived values or excessive side-effect complexity. Developers use watch with a setter when computed with a setter is cleaner.

Solution

Use computed() for derived values. Use watch() for side effects triggered by state changes:

import { ref, computed, watch, watchEffect } from 'vue';

const firstName = ref('Alice');
const lastName = ref('Smith');

// computed: synchronous derived value — cached, efficient
const fullName = computed(() => ${firstName.value} ${lastName.value});

// computed with setter (two-way binding)
const fullNameWritable = computed({
get: () => ${firstName.value} ${lastName.value},
set: (val) => {
[firstName.value, lastName.value] = val.split(' ');
}
});

// watch: side effects when specific sources change
const userId = ref(1);
watch(userId, async (newId, oldId) => {
userData.value = await fetchUser(newId);
}, { immediate: true });

// watchEffect: auto-tracks all reactive deps used inside
watchEffect(async () => {
// automatically tracks userId.value
userData.value = await fetchUser(userId.value);
});

Why

computed() caches its result and only recomputes when dependencies change — it is synchronous and returns a value. watch() is for async operations, DOM manipulation, and explicit side effects. Misusing watch() for derived values creates race conditions and stale data.

Gotchas

  • computed() cannot be async — use watchEffect for async derived state
  • watch() is lazy by default — add { immediate: true } to run on mount
  • watchEffect() runs immediately and tracks dependencies automatically but gives no oldValue
  • watch() with a getter function: watch(() => obj.prop, cb) tracks obj.prop specifically

Code Snippets

Watching a nested property with a getter

// Watch deep nested object
const settings = reactive({ theme: { color: 'blue' } });
watch(
  () => settings.theme.color,
  (newColor) => applyTheme(newColor)
);

Context

When deciding between computed and watch in Vue 3 Composition API

Revisions (0)

No revisions yet.