Unlocking Vue 3 Reactive Patterns with Composition API

Hoàng Ngô Anh Đức· 28/05/2026 02:12
Unlocking Vue 3 Reactive Patterns with Composition API Cover
Vue 3's **Composition API** and **Reactivity Engine** (rewritten completely using ES6 Proxies) have transformed frontend development. They offer incredible flexibility compared to the old Options API. However, with great power comes great confusion. Beginners and intermediate developers alike often struggle with performance traps, reactive destructuring errors, and memory leaks. In this guide, we'll unlock the deep reactive patterns you need for premium, production-grade applications. ### 1. Ref vs. Reactive: The Ultimate Showdown * **`ref()`:** Wrap any primitive values (String, Number, Boolean) or Objects. It creates a wrapper object containing a single reactive `.value` property. * **`reactive()`:** Works **only** for objects, maps, arrays, or sets. It returns a reactive Proxy of the target object directly, without a `.value` wrapper. #### The Destructuring Trap One of the most common reactive bugs in Vue 3 is destructuring a `reactive` object: ```typescript const state = reactive({ count: 0, user: 'Duc' }); // ❌ WARNING: This breaks reactivity! 'count' becomes a plain number. const { count, user } = state; ``` To solve this, always use `toRefs()` to convert the reactive object's properties into individual refs: ```typescript // ✓ Clean, responsive, and type-safe! const { count, user } = toRefs(state); ``` ### 2. Creating Premium Reusable Composables A Composable is a function that leverages Vue's Composition API to encapsulate stateful logic. Let's build a production-ready `useFetch` composable that handles loading state, errors, and automatic cancellation: ```typescript import { ref, watchEffect, type Ref } from 'vue'; export function useFetch(urlRef: Ref) { const data = ref(null); const error = ref(null); const loading = ref(true); watchEffect(async (onCleanup) => { loading.value = true; data.value = null; error.value = null; const controller = new AbortController(); onCleanup(() => controller.abort()); // Cancel ongoing requests automatically! try { const response = await fetch(urlRef.value, { signal: controller.signal }); if (!response.ok) throw new Error("HTTP error " + response.status); data.value = await response.json(); } catch (e: any) { if (e.name !== 'AbortError') { error.value = e; } } finally { loading.value = false; } }); return { data, error, loading }; } ``` This elegant composable cleans up after itself by automatically aborting the prior request if `urlRef` changes before the server responds!

// Read next

// Reader response

Comments

0 Comments

This article has no comments yet.

Name is limited to 60 characters and comment content to 1000 characters.

Hoàng Ngô Anh Đức

// Author

Hoàng Ngô Anh Đức

Software Engineering Student & Junior Full-Stack Developer

Tôi đang học Software Engineering tại RMIT University Vietnam, có 4 năm học tập tại Canada và kinh nghiệm làm việc remote với các tổ chức Canada. Tôi tập trung xây dựng sản phẩm web thực tế bằng Go, Vue, TypeScript và SQLite.