DEV Community

Mr_SC
Mr_SC

Posted on • Edited on • Originally published at zelig880.com

Comprehensive guide on Watch in Vue 3

In this article, we are going to cover in detail what watchers are and how they can be used in Vue js 3 using the Options API. I usually like to cover both Option API and composition API in the same post, but in this case, there are a few too many differences that would make the article complicated, so I have decided to split it into two separate articles. You can find information on how to use Watch in the composition API in this post: How to use Watch in Vue 3 in Composition API.

In this post, I am going to provide as many details as I can to make it easy to comprehend, but a basic understanding of Vue Js and its lifecycle is beneficial.

What are Watchers in Vue Js

Before we learn how to use watch Vue Js, we should first define what this option actually is and when we should use it.

A watchers provide Vue Js users the ability to produce side effect in reaction to a change in state in one or more reactive variables.

Watch are very similar to computed properties as they are both defined as a feature that allows the user to “watch” for a property or data change. Even if it is common for new Vue developers to get confused between these two options, there is a clear distinction between them.

Computed properties return a value and do not produce any side effects. So for example a Full name could be a computed property or a sum of the available rows can be a computed property. Computed property should do nothing else than produce derived values and never trigger any other action within them.

Watchers on the other hand are purposely meant to be created to produce side effects. So for example recording some logs when the users change a selection, or triggering an API when a certain condition is met. This is a perfect example of watchers as they do not return any value, but just trigger an action as a consequence of one or more reactive property changes.

Watchers are not extremely common and you will probably end up using them just on special occasions, but they are an extremely useful feature for a complex component that relies on side effects (logs, API calls, evaluation from dataset).

Watchers and Vue Js lifecycles

Before we move on to discuss how to use this feature is important to understand when this feature takes place and when it is triggered. Understanding its placement within the Vue lifecycle will not only be beneficial to use this, but it will also help you comprehend advanced use cases.

To fully understand the watch option, we need to learn “what” triggers it, and “when” the triggered method takes place.

What triggers a watch to be called

As we have previously mentioned the watch option is triggered by a “change in state”. What this means is that a watch, like computed, is directly related to one or more variables (data, props, computed and even Vuex getters).

When the variable looked at by the watcher changes, the method assigned will be called. Before we move on to try and understand when this actually happens with the Vue lifecycle, we are going to cover a couple of fo simple examples to clarify the above paragraph.

If you have used Vue Js at all, you are well aware that a computed property will re-evaluate as soon as anything that is part of its method block is changed.

computed: {
  fullName() {
    return `${this.firstName} ${this.middleName} ${this.lastNam<span style="background-color: inherit; font-family: inherit; font-size: inherit; color: initial;">e}`</span>;
  }
}
Enter fullscreen mode Exit fullscreen mode

In the above example, the computed property fullName will trigger as soon as either first, middle or lastName is changed. The way in which these “related” variables are declared in a watch method is quite different as the actual name of the watch is the link to the reactive variable:

watch: {
  firstName(){
  }
}
Enter fullscreen mode Exit fullscreen mode

In the above example, a watch method would be triggered if the firstName variable changes. I want to emphasise that watchers and computed are not the same and this example is just used to support the understanding of the feature.

NOTE: you can just listen to a single variable at the time when using watch while using the options API. To listen to more variables you would either have to declare multiple watches or create hacks with computed properties

When is watch triggered

In the above section, we have learned that watchers are actively listening to specific variables and will trigger their method as soon as any of these variables change.

In this section, we are going to analyse the Vue lifecycle and understand at what state are these functions actually triggered. Not knowing when the method is actually triggered is usually the result of dirty code and unnecessary hacks.

To ease of understanding I am going to paste part of the lifecycle diagram from the Vue documentation:

Crop of Vue js lifecycle diagramhttps://vuejs.org/guide/essentials/lifecycle.html#lifecycle-diagramThe reason why I have just passed the middle part of the lifecycle is because watchers are triggered right here at the same time as the beforeUpdate lifecycle hooks are called.

For the reader that has just seen this diagram for the first time, the Mounted lifecycle in the middle of the image symbolizes the component being completely loaded and rendered in the DOM, while the dotted circle around it represent the loop that happens at any time any change of a reactive property of a component (data, property, computed).

The main reason why I wanted to write this section is to emphasise two important points:

  • Watchers are not called when the component is first mounted (there is a special flag to make this happen that we will cover later).
  • Watchers are called “before” the component is re-rendered. So the DOM is still displaying the old values.

Let’s create a simple chronological list of how things would take place to:

  1. Component Instance is called <myComponent firstName=.... />
  2. The component is mounted and displayed in the DOM – NOTE: The watch is NOT called!
  3. The property firstName is changed by the parent
  4. The Component lifecycle started the update cycle
  5. The Watch method is triggered
  6. The Component is re-rendered with the new value

As we will cover later in the article, it is possible to trigger a watch effect after the DOM is re-rendered and there is no need to create any specific hack. I know I have already said above, but it is really important to understand this because the code included in the watch method should never rely on the updated DOM (so we are not supposed to check the DOM or its state).

Real-life examples

Let’s cover a couple of examples and learn more about this Vue Js feature. As mentioned at the start of this article, we are going to cover Option API examples only and we are defining them using the Single File Component (SFC):

...
data() {
  return {
    selected: 0
  }
},
watch: {
  selected(oldValue, newValue) {
    triggerLog(newValue);
  }
}

Enter fullscreen mode Exit fullscreen mode

In the above example, we are triggering a log call as soon as the selected data is changed. Watchers are part of the available option within the Options API and are exposed as an object named watch as seen above.

The name of the watch has to be equal to the variable that we may want to listen to. So in our example, we called the watch “selected” as we want it to be reactive to the “selected” data. Trigger a log after a user interaction is a very common use case for a watch effect.

The watch provides 2 arguments. The first argument includes the new value of the observed variable, while the second includes the old value.

Nested Keys

In the above example, we are observing a single variable, but there are times in which you may want to watch a nested key within a complex object.

To do this, we can use a dot-delimited notation as shown in the following example:

...
data() {
  return {
    user: {
      firstName: '...',
      lastname: '...'
    }
  }
},
watch: {
  'user.firstName'() {
    triggerLog();
  }
}
Enter fullscreen mode Exit fullscreen mode

NOTE: Expressions are not supported and just simple paths should be declared.

Deep

Until now we have always looked at a single value (either directly or by selecting a specific key of an object). This was not done to simplify the example, but it is actually due to a limitation in the watch option.

In its default form, a watcher will not react if a complex (deep) object is passed to it. Luckily for us, observing complex objects is very simple as it just takes a simple configuration called “deep” to be defined.

...
data() {
  return {
    user: {
      firstName: '...',
      lastname: '...'
    }
  }
},
watch: {
  user: {
    handler(newValue, oldValue) {
      //both newValue and oldValue are the FULL object and not just what changed of it!
    },
    deep: true
  }
}
Enter fullscreen mode Exit fullscreen mode

To be able to declare further configurations like “deep” we have to declare our watchers in a different format by declaring the method within a function called “handler” as shown above.

You may be asking yourself why the hassle of having to declare the watch as “deep” and what is the reason behind not making it default. As mentioned in the Vue documentation, the reason behind this decision has to do with the computation complexity required when “deep” is used.

Observing objects requires traversing the object properties and this can be very complex for large objects and should be used with caution.

Immediate – eager

It is not time to cover another important configuration available within the watch option. This one is called “immediate” and it is used to inform the Vue JS framework to trigger our watch immediately as soon as the component is mounted.

If we wanted to re-use the lifecycle diagram as shown before we would need to expand it as using the “immediate” option would mean that the first instance of the watch would actually happen before the component is fully mounted.

Below is a simple example of the use of watch that would trigger immediately:

props: {
  termsAccepted: Boolean
},
watch: {
  user: {
    handler(newValue) {
      if( newValue === true ) triggerLog();
    },
    immediate: true
  }
}

Enter fullscreen mode Exit fullscreen mode

Flush

We have reached the last option available within this Vue Js feature. As we mentioned before, watch are triggered before the component is fully re-rendered, but this can actually be changed using the “flush” configuration.

Using “flush” will make sure that our watcher is called after the component is fully re-rendered and should be used for methods that require the DOM to be fully updated with the new values.

...,
watch: {
  user: {
    handler(newValue) {
      this.$refs.test.style.....
    },
    flush: true
  }
}
Enter fullscreen mode Exit fullscreen mode

Summary

I have used Vue JS for many years, but just recently was really made aware of all the methods available when using the watchers feature. The above post is hopefully going to help you in using this feature correctly and avoid hacky solutions for problems that can easily be fixed with the use of a single setting.
It is time to say goodbye and as always, please make sure to leave me a comment or feedback to improve this post for future readers and subscribe to my newsletter to be notified of future posts.

Top comments (0)