Explain why destructuring a reactive object in Vue breaks reactivity, how Vue’s Proxy-based tracking works, and how to use toRefs() and toRef() to safely extract reactive properties without losing updates.
Why does destructuring break reactivity in Vue? Explain toRefs, toRef, and how to safely extract reactive state
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Overview
In Vue 3, reactive() returns a Proxy that tracks property access and mutation. When you destructure properties from that object, you pull plain values out of the Proxy, so Vue can no longer track reads or writes. The fix is to use toRefs() or toRef(), which keep each extracted property linked to the original reactive source.
1. The core problem: how Vue tracks reactivity
Vue tracks reactivity by intercepting property access and property mutation on a Proxy. If you stop reading or writing through the Proxy, Vue can’t see what you’re doing anymore.
2. The classic bug: destructuring a reactive object
This looks innocent, but it breaks the reactive connection:
import { reactive } from 'vue';
const state = reactive({ count: 0 });
// ❌ Trap: destructuring pulls out a plain number
const { count } = state;
state.count++; // state updates
console.log(count); // still 0 (stale)
Why? Because count is now just a number, not a reactive reference. Vue is no longer involved.
3. The mental model
- reactive() = a smart object (Proxy) that Vue watches.
- Destructuring = copying values out of that object.
- Copies are not reactive, so Vue loses the connection.
4. The solution: toRefs()toRefs() converts each property of a reactive object into a ref that stays connected to the original object.
import { reactive, toRefs } from 'vue';
const state = reactive({ count: 0, name: 'Ada' });
// ✅ Safe: each field is now a ref linked to state
const { count, name } = toRefs(state);
count.value++; // updates state.count
state.count++; // updates count.value
5. toRef(): when you only need one property
If you only want to extract a single property, use toRef() instead of converting everything.
import { reactive, toRef } from 'vue';
const state = reactive({ count: 0, name: 'Ada' });
// ✅ Create a single reactive ref linked to state.count
const count = toRef(state, 'count');
count.value++; // updates state.count
state.count++; // updates count.value
6. Common place this bites people: composables
If a composable returns a reactive object and you destructure it in the consumer, you silently lose reactivity.
// useCounter.js
import { reactive } from 'vue';
export function useCounter() {
const state = reactive({ count: 0 });
return state;
}
// ❌ In component
const { count } = useCounter(); // loses reactivity
Fix: either return toRefs(state) from the composable, or wrap the result with toRefs() at the call site.
// useCounter.js (better)
import { reactive, toRefs } from 'vue';
export function useCounter() {
const state = reactive({ count: 0 });
return toRefs(state);
}
// In component
const { count } = useCounter(); // ✅ stays reactive
7. How this relates to ref()refs are already safe to destructure because the reactivity lives in .value, not in a Proxy identity.
import { ref } from 'vue';
const count = ref(0);
const { value } = count; // just a number copy
// But normally you pass around the ref itself, not destructure .value
8. Practical rules of thumb
- Never destructure a
reactive()object directly. - If you want to extract fields: use
toRefs()ortoRef(). - If you want to freely destructure and pass values around, consider using
refs instead of a single reactive object. - Composables should usually return
toRefs(state), not the raw reactive object.
Intuition: reactivity lives in the Proxy, not in the values. Destructuring takes the values and leaves the magic behind.
Summary
- Destructuring breaks reactivity because it extracts plain values from Vue’s Proxy.
toRefs()converts each property into a ref that stays linked to the source object.toRef()creates a single linked ref for one property.- Use these helpers whenever you need to extract or return pieces of reactive state safely.