How to watch for nested data in Vue Js

Watching an item for changes in Vue Js is quite simple, but things can be slightly more complicated if the item you are watching is part of a nested object. In this article, we are going to cover different methods to help you react to changes to nested watch properties.

What is “watch”

As with all my posts, I always like to give some more explanation for people that may need it. If you already know what “watch” is in Vue Js, feel free to jump to the next section.

If you are a Vue developer, you may probably know that Vue is great because of its reactivity system that really helps us write clean code. There are cases in which you are in need to watch for a specific value change and trigger a side effect. When this scenario occurs, “watch” is the correct solution!

It is important to emphasise that you should just use “watch” if you want to actually trigger a side effect that has nothing to do with the current value being watched. If on the other hand, you wish to modify the value being watched, you should use a “computed” property.

So a definition of “watch” is:

“Watch” is a feature offered in both the option and the compositionb API in Vue JS to allow users to listen to a change in a specific value (data, computed or props) and trigger an callback when this value changes.

Simone Cuomo

If you need further information on what a watch is, I suggest you read the official Vue documentation.

Real-life deep object example

Now that we know what a watch is. It is time to jump over and try to solve our problem and explain how to use this feature in the presence of nested data.

Let’s define a real-life problem for our issue. In the following code, we have a component that includes “data” for a player. This data is nested and includes information such as name and life, but also a nested array with the played “skills”.

An example component may look like this:

export defult {
  data() {
    return {
      player: {
         name: 'Test',
         life: 100,
         skills: ['freeze', 'fire'] 
      }
    }
  }
}

In the following few sections, we are going to see how we can listen for the “skills” array for changes to be able to trigger an API request (remember no side effect to the object itself).

How to watch for deep object

There are a couple of ways to listen to nested objects, neither of these is preferred to any other and they all depend on personal preference and the actual code that is required.

Using dot delimited path

The first method I will introduce is the usage of the Object dot notation, well known within the Javascript ecosystem.

A watch name can actually be defined using the dot notation to actually define our value. So in our example, this would become:

export defult {
  data() {
    return {
      player: {
         name: 'Test',
         life: 100,
         skills: ['freeze', 'fire'] 
      }
    }
  },
  watch: {
    player.skills( newValue, oldValue ) {
      fakeApiCall();
    }
  }
}

As you can see this method is very simple to define and relatively clean. Unfortunately, this has some limitations as it does not allow us to define complex expressions for example passing variables to the object player[myVariable] or trying to access specific indexes player.skills[0].

Watch nested objects using the “deep” property

Vue offers the possibility to watch for a deep object (there is no limit on how deep the object must be), by providing a feature flag called “deep”. I have intentionally not provided this as the first method because it is very resource intensive and it should just be used if you need to watch the whole object and not just a child value.

export defult {
  data() {
    return {
      player: {
         name: 'Test',
         life: 100,
         skills: ['freeze', 'fire'] 
      }
    }
  },
  watch: {
    player: {
      handler ( newValue, oldValue ) {
        fakeApiCall();
      },
      deep: true
  }
}

As you can see, to be able to pass extra features like “deep” and “flush” or “immediate” we have to change the way we define our watched by declaring it as an object with a “handler” function. Before we move on, I want to emphasise that in our case the use of “deep” is actually incorrect as we just care about the skills and not the whole object.

Watch for nested objects using computed properties

If you have used Vue for some time, you are probably aware that you are able to “watch” not only data or props but also computed properties. In this section, we are going to use a computed property to simplify our “watch” declaration.

export defult {
  data() {
    return {
      player: {
         name: 'Test',
         life: 100,
         skills: ['freeze', 'fire'] 
      }
    }
  },
  computed: {
    skills() {
      return this.player.skills;
    }
  },
  watch: {
    skills( newValue, oldValue ) {
      fakeApiCall();
    }
  }
}

I personally like the above method, as it helps me keep the “watch” declaration simple to read and maintain. Furthermore, using a watcher can also allow you to use complex expressions not available with the previous methods. For example, we could create a watcher that returns just the first skill.

Conclusion

“Watch” is very powerful, but it is not always used to its full potential. This post is aiming at providing some guidance to really improve the usage of the “watch” feature in your day-to-day activities. If you enjoyed this article and want to support other readers, post below a comment showing how you have used “watch” in your project,

🤞 Don’t miss these tips!

No spam emails.. Pinky promise!