Vue3: how to declare reactive states
Tags: webdev, vue, frontend, programming
Published: 2024-03-06 21:16:38

> <small>_[Ler em pt/br](https://dev.to/sucodelarangela/vue3-como-declarar-estados-reativos-1lc7)_</small>

In a **Single Page Application (SPA)**, reactivity is the ability of the application to dynamically respond to changes in its data, updating parts of the page without the need to reload it from scratch.

When Vue detects changes in its states, it utilizes the virtual DOM to re-render the interface and ensure that it is always up-to-date, making the process as fast and efficient as possible.

There are different ways to declare reactive states in a Vue application, which may vary depending on the API in use (Options or Composition), as well as the data types themselves. That's what we'll cover in this article!

> _**Table of Contents**
> Reactive data with Options API:
> [Data properties](#data-properties)
> [Computed Properties](#computed-properties)
> [Difference between methods and computed properties](#difference-between-methods-and-computed-properties)
> [Reactive data with Composition API:](#reactive-data-with-composition-api)
> [ref()](#ref)
> [reactive()](#reactive)
> [reactive() limitations](#reactive-limitations)
> [computed()](#computed)
> [Wrapping it up...](#wrapping-it-up)_

![Chemical reaction](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6bsu913siqftcjwhax9t.gif)

---

## Reactive data with Options API

### Data properties

With the Options API, we declare reactive states of a component through **data properties**, using the `data` method for that purpose. This method <mark>returns an object with all reactive states and their values</mark>.

```vue
<script>
export default {
  data () {
    counter: 0
  }
}
</script>
```

In the example above, `counter` is a reactive data, and if modified, the interface will automatically update to reflect the changes.

To see reactivity in action, let's create a method (or function) that modifies the value of `counter` each time we click a button.

```vue
<script>
export default {
  data () {
    counter: 0
  },
  methods: {
    increaseCounter() {
      this.counter++
    }
  }
}
</script>
```

![Counter updating](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2eq8ic66306ayz35qpex.gif)

Note that, upon clicking the button, the value of `counter` is automatically updated in the interface.

It's important for all reactive data to be used in the component to be declared in the `data` option, even if they don't have a defined value yet, as they will be instantiated in the component as soon as it is created (allowing the use of `this` in methods and [lifecycle hooks](https://dev.to/sucodelarangela/vuejs-ciclo-de-vida-dos-componentes-2ko9)).

If you need to initialize a state without a defined value, you can use `undefined`, `null`, or any other value that serves as a placeholder (such as an empty string, for example).

---

### Computed Properties

**Computed properties** are used when we need to <mark>perform calculations or other logic based on `data properties` in a passive manner</mark>. In other words, when a reactive data is changed, all computed properties that depend on that data are also updated. It's as if they were formatting functions for variables.

To declare computed properties, we use the function syntax within the `computed` option. This function should **always return a value**.

```vue
<script>
  export default {
    // hidden code
    computed: {
      doubleCounter() {
        return this.counter * 2
      }
    }
  }
</script>
```

Note that we did not use the arrow function syntax in our computed property because they do not have `this` in their context, preventing us from accessing our data properties.

The computed property above always returns twice the value of `counter`. We can see in our application that whenever `counter` is modified, the value of `doubleCounter` is automatically updated:

![doubleCounter updating](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nz9rr32zht4edx4md4h4.gif)

The same result could be achieved directly in the `template` of our component by inserting the logic within the interpolation (`{{ }}`, also called "mustache").

```vue
<template>
  <p>The double of counter is: {{ doubleCounter }}</p>
  <!-- would become -->
  <p>The double of counter is: {{ counter * 2 }}</p>
<template>
```

This way, our application would behave the same. However, in larger applications, these logics can become much more complex, and with the use of computed properties, we can make our `template` more understandable and our code more organized.

---

### Difference between methods and computed properties

It's easy to see that the same logic of `doubleCounter` could be achieved through a method instead of a computed property, right?

```vue
<template>
  <p>{{ doubleCounter() }}</p>
<template>

<script>
// hidden code
  methods: {
    doubleCounter() {
      return this.counter * 2
    }
  }
</script>
```

With the code above, we could easily achieve the same result. And why didn't we choose this alternative? Because computed properties are cached based on the reactive data they depend on.

**_What does that mean?_**

It means that, as long as `counter` is not modified, `doubleCounter` will not change either and will always return the last computed result, regardless of how many times you re-render your component. On the other hand, methods will always be executed when the component is re-rendered, even if our reactive data `counter` does not change.

> Why do we need caching? Imagine we have an expensive computed property list, which requires looping through a huge array and doing a lot of computations. Then we may have other computed properties that in turn depend on list. Without caching, we would be executing list’s getter many more times than necessary! — [Extract from Vue docs.](https://vuejs.org/guide/essentials/computed.html#computed-caching-vs-methods)

So, let's use methods only when we don't need caching! 😉

---

## Reactive data with Composition API

In Vue 3, reactivity is achieved through the system known as "Proxy-based Reactivity," which creates [proxies](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Proxy) around data objects, allowing the interception of read (_get_) and write (_set_) operations on the properties of these objects. This system is an evolution of the reactivity system used in previous Vue versions (which used `Object.defineProperty`), providing more efficient performance with fewer limitations.

As a result, with the Composition API we have different ways to declare reactive states. Here the data properties come out and the `ref()` and `reactive()` functions come into play!

---

### ref()

To declare reactive data using the `ref()` function, we should use the variable declaration syntax with `const`. `ref()` is commonly used for states with primitive types (such as `string`, `number`, `boolean`, etc.). Let's use the same example of `counter`:

```vue
<script setup>
  import { ref } from "vue";

  const counter = ref(0);

  const increaseCounter = () => {
    counter.value++;
  };
</script>
```

When we studied the [differences between Options and Composition API](https://dev.to/sucodelarangela/vue3-options-api-vs-composition-api-1j09), we saw that we need to import the native Vue functions we are going to use in our component. So, we start by importing `ref` and using it in our `counter` variable to encapsulate the initial value of our state.

To change the value of `counter`, we create an arrow function that modifies the `.value` property of our `ref`. This property is automatically created by the Vue 3 reactivity system, and it is not necessary to declare it when creating our state with `ref()`. This property allows Vue to detect whether a state has been accessed or modified.

![Counter updating](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwn5oegr6lzxedcnwtyd.gif)

Note that when we use our computed state directly in the `template`, there is no need to use the `.value` property.

---

### reactive()

`reactive()` is the second way Vue provides for declaring reactive data. Unlike `ref`, which encapsulates the value of the data in an internal object with the `.value` property, the `reactive()` function makes object-type data reactive. Therefore, `reactive()` has the limitation of not accepting strings, numbers, and booleans.

Using our `counter` example, we can refactor our code to use `reactive()`:

```vue
<script setup>
  import { reactive} from "vue";

  const state = reactive({ counter: 0 });

  const increaseCounter = () => {
    state.counter++;
  };
</script>
```

In the code above, our variable `state` now receives an object `{ counter: 0 }` as reactive data. In this case, if we want to change the value of `counter`, we can directly access the `counter` key within the `state` object in our `increaseCounter` method.

![State counter updating](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j6pmeeq66apjcbjotarp.gif)

Note that in our `template`, we also need to access `state.counter` directly so that its value is correctly rendered on the screen.

---

### reactive() limitations

As mentioned earlier, the first limitation of the `reactive()` function is not accepting `string`, `number`, and `boolean`. Therefore, this function should only be used for object-type data (such as objects themselves, arrays, and collections like `Map` and `Set`).

It's also not possible to reassign a new value to the same reactive object. In other words, you cannot replace the entire object with another object because the Vue reactivity system works directly with property access.

```js
// It's not possible to reassign state
let state = reactive({ count: 0 })
state = reactive({ count: 1 })
```

The use of `reactive()` also does not allow for destructuring because the reactivity connection reference is lost:

```js
const state = reactive({ count: 0 })
// count is disconnected from state.count when destructured
let { count } = state
```

---

### computed()

The use of computed properties serves the same purpose as seen earlier: performing calculations and logic passively according to the detected change in a `ref()` or `reactive()`.

However, with the Composition API, we have a different syntax for creating these computed properties: `computed()` is now a function that takes another function as a parameter and returns a computed `ref`. Let's look at an example:

```vue
<script setup>
import { ref, computed } from "vue";

// hidden code

const doubleCounter = computed(() => {
  return counter.value * 2;
});
</script>
```

In the code above, we declare our computed property as a variable and use the `computed()` function to encapsulate the arrow function containing our logic.

![doubleCounter updating](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vnubu4j49bxlq640q4xk.gif)

---

## Wrapping it up...

We've explored the nuances of creating reactive data in Vue, diving into the syntax of the Options API and Composition API. When exploring the creation of data and computed properties, we've touched the essence of the Options API, where familiarity with data properties and computed properties proves crucial. Additionally, we've ventured into the Composition API, where ref, reactive and computed offer powerful tools for structuring reactive logic in a more modular and concise way.

Now that you know how to declare reactive data, expanding your repertoire to handle reactivity in Vue and build more robust and efficient applications, you're ready to take your Vue development experience to new heights. Experiment, innovate and enjoy the reactive journey!