Interview answer drill

Use this Vue interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.

Why does destructuring break reactivity in Vue? Explain toRefs, toRef, and how to safely extract reactive stateFrontend interview answer

HighIntermediateVue
Interview focus

This Vue interview question tests whether you can explain Vue destructuring and reactivity: Proxy internals, toRefs, and stale-value debugging, connect it to production trade-offs, and handle common follow-up questions.

  • Vue destructuring and reactivity: Proxy internals, toRefs, and stale-value debugging explanation without falling back to memorized docs wording
  • Composition API and Reactivity reasoning, edge cases, and production failure modes
  • How you would answer the most likely Vue interview follow-up
Practice more Vue.js interview questions
Interview quick answer

Explain why destructuring breaks Vue reactivity under the hood, how Proxy tracking works, and how to debug stale values safely with toRefs() and toRef() in composables or components.

Full interview answer

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 them. This is a classic production and interview debug problem: the state looks correct at the source, but the destructured value goes stale. The fix is to use toRefs() or toRef(), which keep the 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:

JAVASCRIPT
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.

JAVASCRIPT
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.

JAVASCRIPT
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.

JAVASCRIPT
// 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.

JAVASCRIPT
// 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.

JAVASCRIPT
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() or toRef().
  • 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

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.

Similar questions
Guides
Preparing for interviews?

Use this as one explanation rep, then continue with the Vue.js interview questions cluster or a guided prep path.