Unlocking Vue 3 Reactive Patterns with Composition API
Hoàng Ngô Anh Đức·
28/05/2026 02:12
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
Related articles
Tailwind CSS v4 & Next-Gen Frontend Styling
Discover the future of utility-first CSS. Explore Tailwind v4's CSS-first engine, major performance boosts, and logical...
// Reader response
Comments
This article has no comments yet.
// 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.