Files
stack/packages/mosaic/framework/skills/vue-best-practices/reference/reactive-destructuring.md
T
fargo 1a822493ba format: apply repo prettier (3.8.1) to the folded skills tree
963 markdown files reformatted with the repository's pinned prettier so
pnpm format:check covers the folded tree like every other repo file.

The formatter's embedded-language pass also normalized code fences
(TS semicolons, closed HTML tags in examples, lowercased CSS hex colors,
one renumbered list that skipped an index). Alphanumeric token deltas vs
the fold commit were audited file-by-file; all are formatter-equivalent
markup normalizations plus the four sanitized skills.
2026-08-19 14:37:17 -05:00

2.6 KiB

title, impact, impactDescription, type, tags
title impact impactDescription type tags
Never Destructure reactive() Objects Directly HIGH Destructuring reactive objects breaks reactivity - changes won't trigger updates capability
vue3
reactivity
reactive
composition-api
destructuring

Never Destructure reactive() Objects Directly

Impact: HIGH - Destructuring a reactive() object breaks the reactive connection. Updates to destructured variables won't trigger UI updates, leading to stale data display.

Vue's reactive() uses JavaScript Proxies to track property access. When you destructure, you extract primitive values from the proxy, losing the reactive connection. This is especially dangerous when destructuring from composables or imported state.

Task Checklist

  • Never destructure reactive objects directly if you need reactivity
  • Use toRefs() to convert reactive object properties to refs before destructuring
  • Consider using ref() instead of reactive() to avoid this pitfall entirely
  • When importing state from composables, check if it's reactive before destructuring

Incorrect:

import { reactive } from 'vue';

const state = reactive({
  count: 0,
  name: 'Vue',
});

// WRONG: Destructuring breaks reactivity
const { count, name } = state;

// These updates work on the original state...
state.count++; // state.count is now 1

// ...but the destructured variables are NOT updated
console.log(count); // Still 0! Lost reactivity
// WRONG: Destructuring from a composable
function useCounter() {
  const state = reactive({ count: 0 });
  return state;
}

const { count } = useCounter(); // count is now a non-reactive primitive

Correct:

import { reactive, toRefs } from 'vue';

const state = reactive({
  count: 0,
  name: 'Vue',
});

// CORRECT: Use toRefs() to maintain reactivity
const { count, name } = toRefs(state);

state.count++;
console.log(count.value); // 1 - Reactivity preserved! (note: now needs .value)
// CORRECT: Return toRefs from composables
function useCounter() {
  const state = reactive({ count: 0 });
  return toRefs(state); // Now safe to destructure
}

const { count } = useCounter(); // count is now a ref, reactivity preserved
// ALTERNATIVE: Just use ref() to avoid the issue entirely
import { ref } from 'vue';

const count = ref(0);
const name = ref('Vue');

// No destructuring needed, no gotchas

Reference