ref() .value Box Wrapper vs reactive() Proxy Unwrap
- Last updated
- Prerequisites:
- Vue 3 Composition API
- JavaScript Proxy fundamentals
- vue
- reactivity
- ref
- reactive
- proxy
- composition-api
Read at your depth
The practical view
`ref()` wraps any value (primitives like `0`, `'hello'`, or objects) in a `{ value: T }` object. In `<template>`, refs are unwrapped automatically (use `count`), but in `<script>` you must use `count.value`. `reactive()` only accepts objects/arrays and returns a deep Proxy: `state.count`. Destructuring a `reactive()` object (`const { count } = state`) breaks reactivity; use `toRefs(state)` to fix it.
The same idea in other frameworks
`const [count, setCount] = useState(0)` (immutable state values).`const count = signal(0)` (read via `count()`, update via `count.set()`).`const count = ref(0)` (read/write via `count.value`).`let count = $state(0)` (direct assignment via Svelte 5 runes).Legacy vs modern
Destructuring reactive() (Broken) vs toRefs / ref() (Safe)
Destructuring a `reactive()` object strips Proxy tracking; `ref()` and `toRefs()` preserve live reactivity.
const { count } = state; // ❌ Reactivity permanently lost!const count = ref(0); // ✅ NEW: Safe to pass and destructureInterview gotchas
Context
This tests JavaScript reference vs primitive value passing and Vue's reactivity mechanics.
The mechanical answer
`reactive()` relies on JavaScript `Proxy` traps (`get` and `set`) on the root object. When you destructure `const { count } = reactive({ count: 0 })`, you are copying the primitive value `0` into a standalone local variable. Plain variables have no getters or setters, so Vue cannot track reads or writes. `toRefs()` converts every property of the reactive object into a separate `ref` whose `.value` getter points back to the original reactive object's property.
Trap
Never use ES6 destructuring directly on a `reactive()` object unless you wrap it with `toRefs()` or `toRef()`.
Context
Checks understanding of automatic ref unwrapping in nested reactive objects.
The mechanical answer
When a `ref` is nested as a property of a `reactive()` object, Vue **automatically unwraps it**. You can read and write `state.count` directly without using `.value`: `state.count = 1`. However, this automatic unwrapping does *not* apply when the ref is an element of a reactive Array (e.g. `const arr = reactive([ref(0)]); arr[0].value`).
Trap
Refs inside reactive arrays or native collections (`Map`, `Set`) do NOT auto-unwrap; `.value` is still required.