Module: Vue Composition API

Refs and reactive

Vue Composition API: Refs and Reactive

The Composition API in Vue 3 introduces powerful ways to manage state and logic within components. Two core concepts for reactive state management are ref and reactive. Let's dive into each, their differences, and how to use them.

1. ref

  • Purpose: ref is used to create a reactive reference to a single value. It's ideal for primitive types (numbers, strings, booleans) and objects where you want to track changes to the value itself.

  • How it works:

    • ref() takes an initial value as an argument.
    • It returns a reactive object with a .value property. You access and modify the actual value through this .value property.
    • Vue automatically tracks dependencies on the .value property, triggering updates when it changes.
  • Example:

<template>
  <p>Count: {{ count }}</p>
  <button @click="increment">Increment</button>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0); // Create a reactive ref with initial value 0

    const increment = () => {
      count.value++; // Increment the value through .value
    };

    return {
      count,
      increment
    };
  }
};
</script>
  • Key Points:
    • Always access and modify the value using .value. Directly assigning to count (without .value) will not make the component reactive.
    • ref is best for simple values and when you need to know when a specific value changes.
    • When passing a ref to a child component, Vue automatically unwraps the .value for you. You don't need to pass count.value.

2. reactive

  • Purpose: reactive is used to create a reactive object. It's best suited for complex objects (like arrays or nested objects) where you want to track changes to the object's properties.

  • How it works:

    • reactive() takes an object as an argument.
    • It returns a proxy of the original object. This proxy is reactive.
    • Changes to the properties of the reactive object are automatically tracked, triggering updates.
    • Unlike ref, you access and modify properties directly on the reactive object, without using .value.
  • Example:

<template>
  <p>Name: {{ person.name }}</p>
  <p>Age: {{ person.age }}</p>
  <button @click="updateAge">Update Age</button>
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const person = reactive({
      name: 'Alice',
      age: 30
    });

    const updateAge = () => {
      person.age++; // Update the age directly on the reactive object
    };

    return {
      person,
      updateAge
    };
  }
};
</script>
  • Key Points:
    • Access and modify properties directly (e.g., person.age).
    • reactive is ideal for complex objects with multiple properties.
    • When passing a reactive object to a child component, you should pass the entire object. Vue does not automatically unwrap it like it does with ref.
    • reactive cannot be used with primitive types directly. You'd need to wrap them in an object first. (e.g., reactive({ count: 0 }))

3. ref vs. reactive: A Comparison

Feature ref reactive
Purpose Single reactive value Reactive object
Initial Value Any type Object
Access/Modify .value property Direct property access
Best For Primitives, simple values Complex objects, nested data
Unwrapping Automatic in child components Manual (pass the entire object)

4. Converting Between ref and reactive

Sometimes you might need to convert between these two types.

  • toRef: Creates a ref for a specific property of a reactive object. This ref is linked to the original property, so changes to the ref.value will update the original object, and vice-versa.
import { reactive, toRef } from 'vue';

const state = reactive({ count: 0 });
const countRef = toRef(state, 'count');

countRef.value++; // Updates state.count as well
console.log(state.count); // Output: 1
  • toRefs: Converts all properties of a reactive object into individual refs. This is useful when you want to destructure a reactive object and pass individual reactive properties to child components.
import { reactive, toRefs } from 'vue';

const state = reactive({ count: 0, message: 'Hello' });
const { count, message } = toRefs(state);

count.value++; // Updates state.count
message.value = 'Goodbye'; // Updates state.message

5. Important Considerations

  • Reactivity System: Both ref and reactive rely on Vue's reactivity system. Changes are tracked through proxies and dependency tracking.
  • Deep Reactivity: reactive provides deep reactivity for nested objects. Changes within nested properties will trigger updates. ref only provides reactivity for the value it holds; nested properties within that value might not be reactive unless you also use reactive or ref on them.
  • Performance: While both are efficient, be mindful of excessive use of reactive with very large objects, as it can impact performance. Consider using ref for individual properties when appropriate.

By understanding ref and reactive, you can effectively manage state and build dynamic, responsive Vue components using the Composition API. Choose the appropriate tool based on the complexity of the data you're working with.