VueJS works on the concept of reactivity. Vue 3 supports reactivity when we use both Options API or Composition API. In this post, we will look at VueJS Reactivity using Options API.

But what is Reactivity in VueJS?

Reactivity in VueJS is the approach in which changes to application state are automatically reflected in the DOM. Basically, it is a mechanism that keeps a datasource in sync with the view layer of the application automatically. Each time there is a change to the datasource or the model, the view is re-rendered to reflect the changes. As a result, reactivity is a powerful tool to build highly-interactive web applications.

1 – Vue JS Data Option – Reactive State

In Options API approach, we define reactive state using the data options. The data option is basically a function that returns an object.

See below example:

export default {
  // Properties returned from data() becomes reactive state
  // and will be exposed on `this`.
  data() {
    return {
      appStatus: {
        currentCounterStatus: '',
      },
      appName: 'Counter Application',
      counter: 0,
    };
  },
}

The object contains properties such as appStatus, appName and counter. When Vue creates an instance of this component, this object is wrapped in the VueJS reactive system. In other words, any changes to the values of these properties will trigger a re-render.

Note that VueJS adds these properties only when the component instance is ready. Therefore, in case a certain value is not available, it is advisable to return null or undefined. Alternatively, you can also assign a placeholder value.

We can access the properties in the reactive object by using this keyword. We can also add additional properties to the this scope. However, those properties won’t be reactive in nature.

2 – Vue 3 Methods with Options API

In the previous section, we looked at the VueJS reactive state. But how we can modify the reactive state?

In Options API approach, we use the methods option to bind the desired methods to the component context.

methods: {
    increment() {
      this.counter++;
      this.appStatus.currentCounterStatus = 'Incremented';
    },
    decrement() {
      this.counter--;
      this.appStatus.currentCounterStatus = 'Decremented';
    },
},

In the above snippet, we declare two methods increment() and decrement(). We can use these methods within the template. For example, when the user clicks the buttons, the appropriate methods will be invoked.

<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>

You can read more about VueJS event handling for details. The main point is that VueJS automatically binds the this value for methods. Due to this, the method uses the correct this context when used as an event listener like in the above case. Within the methods, we can access the reactive state variables.

Info Box

We should not use arrow functions when defining methods in the methods section. Arrow functions can cause issues with Vue’s ability to bind to the appropriate this context.

3 – Vue 3 Nexttick for Reactive DOM Update

VueJS Reactivity makes sure that when we mutate the reactive state, the DOM will re-render. However, DOM updates don’t occur synchronously.

VueJS buffers the updates till the next tick in the update cycle. This prevents unnecessary updates in the case of multiple state changes.

We can also leverage the Vue3 nextTick function. See below example:

import { nextTick } from 'vue';
export default {
  // Properties returned from data() becomes reactive state
  // and will be exposed on `this`.
  data() {
    return {
      appStatus: {
        currentCounterStatus: '',
      },
      appName: 'Counter Application',
      counter: 0,
    };
  },

  // Methods are functions that mutate state and trigger updates.
  // They can be bound as event listeners in templates.
  methods: {
    increment() {
      this.counter++;
      console.log(this.$refs.count);
      nextTick(() => {
        console.log(this.$refs.count);
      });
      this.appStatus.currentCounterStatus = 'Incremented';
    },
    decrement() {
      this.counter--;
      this.appStatus.currentCounterStatus = 'Decremented';
    },
  },
}

Within the increment() method, we get access to the count element from the template and log it to the console immediately. However, we also use the Vue 3 nextTick() function to log the element to the console again.

We get the below logs when we run the application and click the Increment button.

HTMLHeadingElement {tagName: "h2", attributes: {…}, innerHTML: "Count is: 0", nodeType: 1…}

HTMLHeadingElement {tagName: "h2", attributes: {…}, innerHTML: "Count is: 1", nodeType: 1…}

Even though the counter value was updated, the first console log printed the old value of 0. However, the console log within the nextTick() callback printed the latest updated value.

This is an important point to keep in mind. When we depend on the DOM state for certain logic, it is good to wait for the DOM state to change to actually propagate before taking any action. The Vue 3 nextTick() function provides a way to do so.

4 – VueJS Deep Reactivity

In VueJS, state is deeply reactive. In other words, Vue can detect changes when we mutate nested objects.

See below example:

  data() {
    return {
      appStatus: {
        currentCounterStatus: '',
      },
      appName: 'Counter Application',
      counter: 0,
    };
  },

  methods: {
    increment() {
      this.counter++;
      console.log(this.$refs.count);
      nextTick(() => {
        console.log(this.$refs.count);
      });
      this.appStatus.currentCounterStatus = 'Incremented';
    },
    decrement() {
      this.counter--;
      this.appStatus.currentCounterStatus = 'Decremented';
    },
  },

The appStatus is a nested object. Even when we modify the currentCounterStatus property, it will trigger a DOM update.

Below is the complete code for this post for reference:

<script>
import { nextTick } from 'vue';
export default {
  // Properties returned from data() becomes reactive state
  // and will be exposed on `this`.
  data() {
    return {
      appStatus: {
        currentCounterStatus: '',
      },
      appName: 'Counter Application',
      counter: 0,
    };
  },

  // Methods are functions that mutate state and trigger updates.
  // They can be bound as event listeners in templates.
  methods: {
    increment() {
      this.counter++;
      console.log(this.$refs.count);
      nextTick(() => {
        console.log(this.$refs.count);
      });
      this.appStatus.currentCounterStatus = 'Incremented';
    },
    decrement() {
      this.counter--;
      this.appStatus.currentCounterStatus = 'Decremented';
    },
  },

  // Lifecycle hooks are called at different stages
  // of a component's lifecycle.
  // This function will be called when the component is mounted.
  mounted() {
    console.log(`The initial count is ${this.counter}.`);
  },
};
</script>

<template>
  <h1>{{ appName }}</h1>
  <h2 ref="count">Count is: {{ counter }}</h2>
  <button @click="increment">Increment</button>
  <button @click="decrement">Decrement</button>
  <h2>{{ appStatus.currentCounterStatus }}</h2>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Conclusion

Reactivity is a key aspect of building applications in Vue 3. In this post, we covered VueJS Reactivity using Options API.

You can play around with the code on Stackblitz.

However, Vue Reactivity also works with the Composition API. Check out this post on Vue 3 Reactivity using Composition API. If you have any comments or queries about this post, please feel free to mention them in the comments section below.

Categories: VueJS

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *