JAVASCRIPT

Mastering Reactive Objects vs. Primitives with `reactive` and `ref` in Vue 3

Understand the core differences and best practices for using `reactive` for objects/arrays and `ref` for primitives in Vue 3's reactivity system to avoid common pitfalls.

<script setup>
import { ref, reactive, watch } from 'vue';

// Using ref for primitives (numbers, strings, booleans)
const count = ref(0);
const message = ref('Hello');

// Using reactive for objects and arrays
const user = reactive({
  id: 1,
  name: 'Alice',
  details: { age: 30, city: 'New York' }
});

const increment = () => {
  count.value++; // Access with .value
};

const changeName = () => {
  user.name = 'Bob'; // Direct access for reactive objects
  user.details.age = 31;
};

// Destructuring reactive objects can lose reactivity if not careful
// INCORRECT: Will lose reactivity
// const { name, id } = user; 
// name = 'Charlie'; // This won't update the original user object

// CORRECT: Using toRefs to retain reactivity when destructuring
import { toRefs } from 'vue';
const { name, id } = toRefs(user);
const changeNameViaToRefs = () => {
  name.value = 'Charlie'; // Now it's reactive
};

// Watching a ref
watch(count, (newVal, oldVal) => {
  console.log(`Count changed from ${oldVal} to ${newVal}`);
});

// Watching a reactive object (deep by default if a specific property is watched)
watch(() => user.name, (newVal, oldVal) => {
  console.log(`User name changed from ${oldVal} to ${newVal}`);
});

// Watching deeply nested properties in a reactive object
watch(user.details, (newVal, oldVal) => {
  console.log('User details changed deeply:', newVal, oldVal);
}, { deep: true });

</script>

<template>
  <div>
    <h3>Ref (Primitives)</h3>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment Count</button>
    <p>Message: {{ message }}</p>

    <h3>Reactive (Objects/Arrays)</h3>
    <p>User Name: {{ user.name }} (via user.name)</p>
    <p>User ID: {{ user.id }}</p>
    <p>User Age: {{ user.details.age }}</p>
    <button @click="changeName">Change User Name & Age</button>

    <h3>Reactive with `toRefs` (for destructuring)</h3>
    <p>User Name (via toRefs): {{ name }}</p>
    <button @click="changeNameViaToRefs">Change User Name (via toRefs)</button>
    <p>User ID (via toRefs): {{ id }}</p>
  </div>
</template>
How it works: This snippet clarifies the usage of `ref()` for primitive values (like numbers, strings, booleans) and `reactive()` for objects and arrays in Vue 3's Composition API. It highlights that `ref` values are accessed/mutated via `.value`, while `reactive` objects are accessed directly. Crucially, it demonstrates how destructuring a `reactive` object directly can lead to loss of reactivity and introduces `toRefs()` as the correct way to destructure reactive properties while preserving their reactivity.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs