Vue

Lesson 03

Computed values versus watchers

Use computed values for derived display data and reserve watchers for side effects.

Good Code

ReviewList.vue
<script setup lang="ts">
import { computed, ref } from "vue";

const query = ref("");
const reviews = ref([{ id: "r1", title: "Auth review" }]);

// Computed output stays derived from the current inputs.
const visibleReviews = computed(() =>
  reviews.value.filter((review) =>
    review.title.toLowerCase().includes(query.value.toLowerCase()),
  ),
);
</script>

<template>
  <input v-model="query" aria-label="Filter reviews">
  <article v-for="review in visibleReviews" :key="review.id">
    {{ review.title }}
  </article>
</template>

Bad Code

ReviewList.vue
<script setup>
import { ref, watch } from "vue";

const query = ref("");
const reviews = ref([{ id: "r1", title: "Auth review" }]);
const visibleReviews = ref(reviews.value);

// A watcher copies derived state and can fall out of sync.
watch(query, () => {
  visibleReviews.value = reviews.value.filter((review) =>
    review.title.toLowerCase().includes(query.value.toLowerCase()),
  );
});
</script>

Review Notes

What to review

Good Code

The good version keeps filtered reviews as a computed value. The list updates whenever either the query or reviews change.

Bad Code

The bad version copies derived data into a separate ref. A reviewer must check every input path to confirm the copied value is refreshed.

Takeaways

  • If a value can be derived from reactive inputs, prefer computed over a watcher that copies state.