Vue.js 101: Mastering Computed Properties and Watchers for Reactive Data
Tags: javascript, beginners, tutorial, vue
Published: 2024-05-29 19:27:27

In Vue.js, computed properties and watchers are powerful tools for managing and reacting to reactive data. Understanding these features is essential for building efficient and responsive Vue.js applications. In this article, we'll explore computed properties and watchers in detail, from beginner to advanced examples, using the Composition API. Let's dive in!

## What Are Computed Properties and Watchers?

### Computed Properties

Computed properties are reactive data properties that depend on other reactive data properties. They are cached based on their dependencies and will only re-evaluate when one of those dependencies changes. Computed properties are ideal for complex logic that depends on reactive data.

**Benefits of Computed Properties:**
- **Caching**: Computed properties are cached based on their dependencies, making them efficient. They only re-compute when one of their dependencies changes.
- **Declarative**: Computed properties provide a clear and declarative way to describe derived state, making your code more readable and maintainable.
- **Simplifies Logic**: They are excellent for encapsulating complex calculations or logic that depends on reactive data.

**When Not to Use Computed Properties:**
- **Side Effects**: Avoid using computed properties for side effects like API calls or logging. Computed properties should be pure and only return values based on their dependencies.
- **Asynchronous Operations**: They are not suitable for asynchronous operations, as they are designed to be synchronous.

### Watchers

Watchers allow you to perform side effects in response to changing reactive data. Unlike computed properties, which are used for derived state, watchers are primarily used for executing code in reaction to changes, such as making API calls or logging.

**Benefits of Watchers:**
- **Flexibility**: Watchers offer more flexibility for executing code in response to data changes. They can handle complex logic that needs to run when data changes.
- **Side Effects**: Ideal for performing side effects like asynchronous operations, logging, or complex data transformations that are triggered by changes in reactive data.

**When Not to Use Watchers:**
- **Simple Computations**: For simple derived state or calculations, use computed properties instead of watchers. Watchers are overkill for these scenarios.
- **Declarative Logic**: If you can achieve the same result with a computed property, it’s often clearer and more maintainable to use the computed property.

## Using the Composition API

### Setting Up the Project

Before we start, make sure you have Vue.js installed. Set up a new project using Vue CLI:

```bash
npm install -g @vue/cli
vue create vue-computed-watcher
cd vue-computed-watcher
npm run serve
```

### Basic Example with Computed Properties

Let's start with a basic example of computed properties using the Composition API.

**Template:**

```html
<template>
  <div>
    <h2>Basic Computed Property Example</h2>
    <p>Original Message: {{ message }}</p>
    <p>Reversed Message: {{ reversedMessage }}</p>
  </div>
</template>
```

**Script:**

```javascript
<script>
import { ref, computed } from 'vue';

export default {
  setup() {
    const message = ref('Hello Vue.js');
    
    const reversedMessage = computed(() => {
      return message.value.split('').reverse().join('');
    });

    return {
      message,
      reversedMessage
    };
  }
};
</script>
```


- **Reactive Data**: `message` is a reactive data property created using `ref`.
- **Computed Property**: `reversedMessage` is a computed property that depends on `message`. It reverses the string whenever `message` changes.

### Advanced Example with Computed Properties

Now, let's create a more advanced example that includes multiple dependencies and demonstrates the power of computed properties.

**Template:**

```html
<template>
  <div>
    <h2>Advanced Computed Property Example</h2>
    <p>Items: {{ items.map(item => item.name).join(', ') }}</p>
    <p>Total Price: {{ totalPrice }}</p>
  </div>
</template>
```

**Script:**

```javascript
<script>
import { ref, computed } from 'vue';

export default {
  setup() {
    const items = ref([
      { name: 'Apple', price: 1.2 },
      { name: 'Banana', price: 0.8 },
      { name: 'Cherry', price: 2.5 }
    ]);

    const totalPrice = computed(() => {
      return items.value.reduce((total, item) => total + item.price, 0).toFixed(2);
    });

    return {
      items,
      totalPrice
    };
  }
};
</script>
```


- **Reactive Data**: `items` is an array of objects, each representing an item with a name and price.
- **Computed Property**: `totalPrice` calculates the sum of item prices, updating automatically when the items array changes.

### Basic Example with Watchers

Watchers are useful for reacting to data changes, such as making an API call or logging information.

**Template:**

```html
<template>
  <div>
    <h2>Basic Watcher Example</h2>
    <input v-model="name" placeholder="Enter your name" />
    <p>Greeting: {{ greeting }}</p>
  </div>
</template>
```

**Script:**

```javascript
<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const name = ref('');
    const greeting = ref('Hello!');

    watch(name, (newValue) => {
      greeting.value = `Hello, ${newValue}!`;
    });

    return {
      name,
      greeting
    };
  }
};
</script>
```


- **Reactive Data**: `name` and `greeting` are reactive data properties.
- **Watcher**: The watcher observes `name` and updates `greeting` whenever `name` changes.

### Advanced Example with Watchers

For an advanced example, let's perform an asynchronous operation in a watcher.

**Template:**

```html
<template>
  <div>
    <h2>Advanced Watcher Example</h2>
    <input v-model="query" placeholder="Search for a GitHub user" />
    <p v-if="loading">Loading...</p>
    <p v-if="error">{{ error }}</p>
    <pre v-if="user">{{ user }}</pre>
  </div>
</template>
```

**Script:**

```javascript
<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const query = ref('');
    const user = ref(null);
    const loading = ref(false);
    const error = ref(null);

    const fetchUser = async (username) => {
      loading.value = true;
      error.value = null;
      user.value = null;

      try {
        const response = await fetch(`https://api.github.com/users/${username}`);
        if (!response.ok) throw new Error('User not found');
        user.value = await response.json();
      } catch (err) {
        error.value = err.message;
      } finally {
        loading.value = false;
      }
    };

    watch(query, (newQuery) => {
      if (newQuery) fetchUser(newQuery);
    });

    return {
      query,
      user,
      loading,
      error
    };
  }
};
</script>
```


- **Reactive Data**: `query`, `user`, `loading`, and `error` are reactive data properties.
- **Watcher**: The watcher on `query` triggers the `fetchUser` function whenever `query` changes, performing an asynchronous API call to fetch GitHub user data.


Computed properties and watchers are indispensable tools in Vue.js for managing and reacting to reactive data. Computed properties are ideal for derived state that depends on other reactive data, providing a clear and efficient way to manage complex logic. Watchers, on the other hand, offer flexibility for executing code in response to data changes, making them perfect for side effects such as API calls.

By understanding and effectively using computed properties and watchers, you can build more efficient, responsive, and maintainable Vue.js applications. Whether you're a beginner or looking to advance your skills, mastering these concepts will significantly enhance your Vue.js development experience. Happy coding!


Twitter:  [@delia_code](https://x.com/delia_code)
Instagram:[@delia.codes](https://www.instagram.com/delia.codes/)
Blog: [https://delia.hashnode.dev/](https://delia.hashnode.dev/)