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

Senior Full-Stack Engineer & Software Architect

Tôi là một kỹ sư phần mềm giàu kinh nghiệm chuyên thiết kế và xây dựng các hệ thống web hiện đại, scalable backend sử dụng Go, Vue.js, TypeScript và kiến trúc đám mây Cloud. Đam mê chia sẻ kiến thức kỹ thuật và tối ưu hiệu năng phần mềm.