beginner Step 3 of 15

Reactive Data and Methods

Vue.js Development

Reactive Data and Methods

Reactivity is the core concept that makes Vue powerful. When you declare data properties in a Vue component, Vue wraps them with a reactive proxy (in Vue 3) that tracks when properties are read and triggers re-renders when they are modified. This means you simply change your data and the DOM updates automatically — no manual DOM manipulation required. Understanding how Vue's reactivity system works under the hood helps you avoid common pitfalls and write more efficient components.

The data() Function

In the Options API, the data() function returns an object whose properties become reactive. Vue processes this object recursively, making all nested properties reactive. The function must return a fresh object to ensure each component instance has its own independent state.

<template>
  <div>
    <h2>{{ user.name }}</h2>
    <p>Age: {{ user.age }}</p>
    <p>Hobbies: {{ user.hobbies.join(", ") }}</p>
    <button @click="birthday">Birthday!</button>
    <button @click="addHobby">Add Hobby</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      user: {
        name: "Alice",
        age: 25,
        hobbies: ["reading", "coding"],
      },
      counter: 0,
      message: "Hello Vue!",
    };
  },
  methods: {
    birthday() {
      // Direct mutation triggers reactivity
      this.user.age++;
    },
    addHobby() {
      // Array methods are reactive in Vue 3
      this.user.hobbies.push("gaming");
    },
  },
};
</script>

Methods

Methods are functions defined in the methods option. They have access to the component instance via this and can read and modify reactive data. Methods are called from templates, event handlers, lifecycle hooks, or other methods.

<template>
  <div>
    <h3>Shopping Cart</h3>
    <div v-for="item in cart" :key="item.id">
      <span>{{ item.name }} - ${{ item.price.toFixed(2) }}</span>
      <span> x {{ item.quantity }}</span>
      <button @click="increaseQuantity(item)">+</button>
      <button @click="decreaseQuantity(item)">-</button>
      <button @click="removeItem(item.id)">Remove</button>
    </div>
    <p><strong>Total: ${{ totalPrice.toFixed(2) }}</strong></p>
    <button @click="clearCart">Clear Cart</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      cart: [
        { id: 1, name: "Widget", price: 9.99, quantity: 2 },
        { id: 2, name: "Gadget", price: 24.99, quantity: 1 },
        { id: 3, name: "Doohickey", price: 4.99, quantity: 3 },
      ],
    };
  },
  computed: {
    totalPrice() {
      return this.cart.reduce(
        (sum, item) => sum + item.price * item.quantity, 0
      );
    },
  },
  methods: {
    increaseQuantity(item) {
      item.quantity++;
    },
    decreaseQuantity(item) {
      if (item.quantity > 1) {
        item.quantity--;
      }
    },
    removeItem(id) {
      this.cart = this.cart.filter((item) => item.id !== id);
    },
    clearCart() {
      this.cart = [];
    },
  },
};
</script>

Reactivity Caveats

Vue 3's Proxy-based reactivity handles most cases automatically, but understanding a few nuances helps avoid surprises.

<script>
export default {
  data() {
    return {
      user: { name: "Bob", age: 30 },
      items: ["apple", "banana"],
    };
  },
  methods: {
    // Adding new properties to reactive objects works in Vue 3
    addProperty() {
      this.user.email = "bob@test.com"; // Reactive in Vue 3!
      // In Vue 2, you would need: this.$set(this.user, "email", "bob@test.com")
    },

    // Array index assignment is reactive in Vue 3
    replaceItem() {
      this.items[0] = "cherry"; // Reactive in Vue 3!
      // In Vue 2, you would need: this.$set(this.items, 0, "cherry")
    },

    // Replacing the entire object or array is always reactive
    resetUser() {
      this.user = { name: "New User", age: 0 };
    },

    // Destructuring loses reactivity — use refs or computed instead
    logName() {
      // BAD: this loses reactivity if used in template
      // const { name } = this.user;
      // GOOD: access through the reactive object
      console.log(this.user.name);
    },
  },
};
</script>
Tip: Never use arrow functions for methods, computed properties, or lifecycle hooks. Arrow functions do not have their own this binding, so this would not point to the component instance. Always use regular function syntax in the Options API.

Key Takeaways

  • The data() function returns an object whose properties are automatically made reactive by Vue.
  • Methods defined in the methods option can read and modify reactive data via this.
  • Vue 3's Proxy-based reactivity handles property additions, array index changes, and nested objects automatically.
  • Destructuring reactive properties breaks reactivity — always access through the reactive object.
  • Never use arrow functions for component options that need this access.