Back

Vue3 Composition API: watchEffect vs. watch

Vue3 Composition API: watchEffect vs. watch

In Vue, watchers are used to watch for changes in the DOM or asynchronous operations. When we see applications with features like clicking a button to close a navbar or inputting user data that fetches an API, that’s possibly a watcher in action. Before the release of Composition API, watch was used in Options API. In this article, we will take a brief look at the Composition API and analyze the watchers in Composition API: watch() function and WatchEffect.

This tutorial requires a good familiarity with JavaScript and a basic understanding of Vue 3.

What is the Composition API?

Composition API is a built-in feature of Vue3, launched in the recent Vue3.0 to correct the errors of mixins and dependency injections that came with Options API. This recent feature makes it easier to build large-scale applications using plain JavaScript or typescript. It is flexible, has a simpler syntax, and requires a good knowledge of reactivity to be used properly. Check out the official documentation.

What is the Watch Option?

This option watches for changes and will only run when it detects any. Watch requires two parameters to run, and it runs when there is a change in value. Let us look at a simple application that generates author bios.

<script>
  export default {
    data() {
      return {
        authorBio: 'My name is Namma'
        };
      },
      methods: {
        async getUser() {
          const res = await fetch('https://quotable.io/authors/?bio')
          const { results } = await res.json()
          const randomNum = Math.floor(Math.random() * results.length-1) 
          const randomItem = results[randomNum]
          this.authorBio = randomItem.bio
        }
      },
      mounted() {
        this.getUser();
      },
    
      watch: {
        authorBio: (newauthorBio,oldauthorBio) => {
          console.log(oldauthorBio);
          console.log(newauthorBio);
        }
      }
    }
</script>

<template>
    <p>{{authorBio}}</p>
</template>

In the code above, watch is tracking the author bio, and as soon as a change is detected, it will log the old and new values to the console. Other features are implemented with the watch Option. You can read more about Watch Option here.

Now, we will look at the newer watch() function and WatchEffect.

What is the Watch() function?

The watch function is like the watch option, but is an upgrade because of the benefits of the Composition API. Watch() function uses a ref as an argument. Let us refactor our previous code using the watch() function.

// script setup
import { ref, watch, onMounted } from 'vue'  

const authorBio = ref('My Name is Namma');
async function getUser() {

  const res = await fetch('https://quotable.io/authors')
  const {results} = await res.json()
  const randomNum = Math.floor(Math.random() * results.length-1) 
  const randomItem = results[randomNum]
  authorBio.value = randomItem.bio
}

onMounted(() => {
  getUser();
}),
    
watch(authorBio, (newauthorBio, oldauthorBio) => {
       if (newauthorBio.startsWith('A')){
         console.log("This Bio starts with A");
       } else {
         console.log('This Bio starts with other letters');
         }
   
       console.log(oldauthorBio);
 })
</script>
<template>
    <p>{{authorBio}}</p>
</template>

We repeat what we did in the first example in this code. We will manipulate the API results using the watch() function. We can also use a getter in the watch() function. In the sample code, we could get a new value from both the previous and current bios.

// script setup
import { ref, watch, onMounted } from 'vue'
  
const authorBio = ref('My Name is Namma');
const description = ref('A writer');

async function getUser() {
    const res = await fetch('https://quotable.io/authors')
    const {results} = await res.json()
    const randomNum = Math.floor(Math.random() * results.length-1)
    const randomItem = results[randomNum]
    authorBio.value = randomItem.bio
    description.value = randomItem.description
}

onMounted(() => {
  getUser();
}),
    
watch(() => authorBio.value + ' ' +description.value,(allBio) => {
  console.log(allBio);

})

</script>

<template>
    <p>{{allBio}}</p>
</template>

Watch will log allBio as soon as it detects changes to the console.

Now, let’s look at watchEffect.

Open Source Session Replay

OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.

replayer.png

Start enjoying your debugging experience - start using OpenReplay for free.

What is watchEffect?

watchEffect watches for changes and performs side effects as well. When using watchEffect, we cannot control the dependencies we are tracking. But, watchEffect runs immediately and watches for changes afterwards. Let us refactor our previous code.

// script setup
import { ref, onMounted, watchEffect } from 'vue'
  
const authorBio = ref('My Name is Namma');
const description = ref('A writer');
const allBio = ref('');

async function getUser() {
    const res = await fetch('https://quotable.io/authors')
    const {results} = await res.json()
    const randomNum = Math.floor(Math.random() * results.length-1)
    const randomItem = results[randomNum]
    authorBio.value = randomItem.bio
    description.value = randomItem.description
}

onMounted(() => {
  getUser();
}),

watchEffect(() => {
    allBio.value = (`${authorBio.value}  ${description.value}`);
    console.log(allBio.value);
})

</script>

<template>
    <p>{{allBio}}</p>
</template>

Unlike the watch() function, watchEffect tracks the allBio.value with no parameters and logs the initial value before logging the changed value.

The Flush Option

watchEffect runs immediately and reruns if there is a change in value before components update. We use the flush post-option if we want some changes to be applied, but not immediately.

watchEffect(() => {
    allBio.value = (`${authorBio.value}  ${description.value}`);
    console.log(allBio.value);
},
{
    flush: 'post';
});

In Vue3.2+ recent release, there is a new feature called WatchPostEffect.

The WatchPostEffect option

This is an alias of flush: post option. If we wish to make some changes in our program after Vue has been updated and not immediately, we will do this:

// script setup
import { ref, onMounted, watchEffect, watchPostEffect } from 'vue'
  
const authorBio = ref('My Name is Namma');
const description = ref('A writer');
const allBio = ref('');
const allBioId = ref(0);

async function getUser() {
    const res = await fetch('https://quotable.io/authors')
    const {results} = await res.json()
    const randomNum = Math.floor(Math.random() * results.length-1)
    const randomItem = results[randomNum]
    authorBio.value = randomItem.bio
    description.value = randomItem.description
}

onMounted(() => {
  getUser();
}),

watchEffect(() => {
    allBio.value = (`${authorBio.value}  ${description.value}`);
    console.log(allBio.value);
})
    
watchPostEffect( () => {
    allBioId.value++;
})
</script>

<template>
    <p>{{allBio}}</p>
    <p>{{allBioId}}</p>
</template>

This will cause allBioId to apply changes after other changes have been applied.

The WatchSyncEffect Option

WatchSyncEffect was released with WatchPostEffect, and this is an alias for the flush:sync option.

watchEffect(() => {
 allBio.value = (`${authorBio.value}  ${description.value}`);
    console.log(allBio.value);
},     
 {
  flush: 'sync';
 })

Using WatchSyncEffect in our program:

// script setup
import { ref, onMounted, watchEffect, watchSyncEffect } from 'vue'
  
const authorBio = ref('My Name is Namma');
const description = ref('A writer');
const allBio = ref('');
const allBioId = ref(0);

async function getUser() {
    const res = await fetch('https://quotable.io/authors')
    const {results} = await res.json()
    const randomNum = Math.floor(Math.random() * results.length-1)
    const randomItem = results[randomNum]
    authorBio.value = randomItem.bio
    description.value = randomItem.description
}

onMounted(() => {
  getUser();
}),

watchEffect(() => {
    allBio.value = (`${authorBio.value}  ${description.value}`);
    console.log(allBio.value);
})

watchSyncEffect( () => {
    allBioId.value++;
})
</script>

<template>
    <p>{{allBio}}</p>
    <p>{{allBioId}}</p>
</template>

WatchSyncEffect forces our program to be synchronous. Instead of applying those changes at a separate timing, they would be applied together. This is not a good practice as it could hinder the flow of your program.

When To Use These Watchers?

It will be up to you to choose which feature you want for your program, but you should note these points.

  • watch option and watch() function takes two values. You could use it when you want more control of the values you are changing. If you do not want to watch for changes immediately, use watch. Watch runs lazily and applies changes when there is a change in a dependency.
  • If you want to track changes in your code, use watchEffect. watchEffect has a simpler syntax than watch, but you cannot manipulate the dependency values easily.
  • You could use WatchPostEffect when you want to make changes after the Vue components updates. You can use the flush: post option in place of WatchPostEffect; you can also use it with watch.

Conclusion

Now, we have understood the watch option, watch() function, and watchEffect on Option API and Composition API. You could experiment with these features until you are comfortable with them. Other features could be implemented with the watchers: debugging, deep watching, and invalidating a watcher, amongst others.

For more, check the Vue Official documentation.