Learn Vue - Reactive State & Composition API
Series/Learn Vue/Episode 5
Episode 5 of 24

Learn Vue - Reactive State & Composition API

This episode dives deep into reactive state management in Vue 3: ref and reactive, computed properties, watchers, lifecycle hooks, and a comparison of the Composition API and Options API, ending with building custom composables for reusable logic.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

In episode 4 you rendered data and responded to events. Now it's time to understand the brain of an application: reactive state. How well you manage state determines how maintainable the application is — and the Composition API in Vue 3 is designed so state and logic can be grouped together neatly.

Episode 5 dissects ref and reactive, computed for derived values, watchers for reacting to changes, and lifecycle hooks. Then we'll compare the Composition API and the Options API, and close with custom composables — a technique you'll use in almost every coding episode that follows.

Reactive State with ref and reactive

ref for Single Values

ref wraps any value into a reactive one. Access the value through the .value property:

JSRef dasar
import { ref } from "vue";
 
const jumlah = ref(0);
jumlah.value = 5;

When jumlah.value changes, every part of the template that depends on it updates too. A ref works for primitives as well as objects, and is always safe to destructure because its value is accessed through .value.

reactive for Objects

reactive wraps an object so its properties become reactive directly:

JSReactive object
import { reactive } from "vue";
 
const user = reactive({
  nama: "Arman",
  skill: ["Vue", "Node.js"],
});
 
user.nama = "Budi";
user.skill.push("TypeScript");

The modifications user.nama = "Budi" and user.skill.push("TypeScript") are registered by the reactivity system right away, including changes inside arrays. Use reactive for objects with many fields, and ref for single values or when you need the automatic unwrapping in templates.

Computed and Watchers

Computed Properties

computed creates a cached derived value that is only recalculated when its dependencies change:

JSComputed property
import { ref, computed } from "vue";
 
const harga = ref(200000);
const diskon = ref(0.1);
const total = computed(() => harga.value * (1 - diskon.value));

total updates automatically when harga or diskon changes, without wasteful recalculations. Because it's cached, computed is more efficient than calling a plain function in a template.

Watchers and watchEffect

watch reacts to one specific change; watchEffect tracks every dependency it reads and runs immediately:

JSwatch dan watchEffect
import { ref, watch, watchEffect } from "vue";
 
const query = ref("");
 
watch(query, (nilaiBaru) => {
  console.log("query berubah:", nilaiBaru);
});
 
watchEffect(() => {
  console.log("query saat ini:", query.value);
});

The key difference: watch(query, ...) only fires when query changes, while watchEffect(...) runs once at the start and then every time any of its dependencies change.

Lifecycle Hooks

Lifecycle hooks execute at specific moments in a component's lifecycle:

JSLifecycle hooks
import { onMounted, onUnmounted, watchEffect } from "vue";
 
onMounted(() => {
  console.log("komponen siap");
});
onUnmounted(() => {
  console.log("komponen dilepas");
});

onMounted is used for initialization such as API calls, and onUnmounted for cleanup such as removing intervals or listeners.

Composition API vs Options API

Two Ways of Writing Components

The Options API organizes logic into the data, computed, and methods options:

JSOptions API
<script>
export default {
  data() {
    return { jumlah: 0 };
  },
  methods: {
    tambah() {
      this.jumlah = this.jumlah + 1;
    },
  },
};
</script>

The Composition API organizes everything within the setup function or <script setup>:

JSComposition API
<script setup>
import { ref } from "vue";
const jumlah = ref(0);
function tambah() {
  jumlah.value = jumlah.value + 1;
}
</script>

When to Use Which

The Composition API is more flexible for related logic because you can group code by function rather than by type. The Options API is easier to read for small components and is still fully supported. Both are valid; this series uses the Composition API because it fits large applications and composables better.

Custom Composables

Wrapping Logic Into Functions

A composable is a function that uses the Composition API and returns reusable state. The most classic example: a counter and timer:

JSComposable useCounter
import { ref, computed } from "vue";
 
export function useCounter(awal = 0) {
  const jumlah = ref(awal);
  const ganda = computed(() => jumlah.value * 2);
 
  function tambah() {
    jumlah.value = jumlah.value + 1;
  }
 
  return { jumlah, ganda, tambah };
}

Save it as src/composables/useCounter.js. The naming convention for composables always starts with use, so you can recognize them at a glance.

Using a Composable in a Component

useCounter(10) creates a new counter instance with an initial value of 10, and every consuming component gets its own separate state.

Summary

Episode 5 taught you to manage state properly: ref and reactive for basic state, computed for efficient derived values, watchers for reacting to changes, lifecycle hooks for initialization and cleanup, and composables for reusable logic.

Key takeaways:

  • Use ref for single values, reactive for objects.
  • computed is cached and only recalculated when dependencies change.
  • watch monitors specific changes; watchEffect tracks everything.
  • onMounted for initialization, onUnmounted for cleanup.
  • The Composition API groups logic by function.
  • Composables prefixed with use wrap reusable logic.

In the next episode 6, we'll cover component communication — props and custom events with defineEmits, provide/inject for dependency injection, slots and scoped slots, plus reusable component patterns for consistent UIs.

Learn Vue - Reactive State & Composition API | Learn Vue