Chimezie Enyinnaya I'm a self-taught software developer based in Lagos, Nigeria. I enjoy teaching what I have learned and what I'm currently learning so that others can benefit from it.

Understanding $nextTick in Vue.js

3 min read 1060

Understanding $nextTick in Vue.js

A frontend developer (let’s say his name is Eric) walks into a Vue bar.

Eric orders his favorite cocktail: the Nuxt. The bartender is working on it. Then he proceeds to rant.

He begins with how he just discovered $nextTick in the Vue 3 documentation under instance methods and is blown away. Eric has been working with Vue for some time and is used to writing $watch and $emit as instance methods. So what’s $nextTick for? The Vue documentation says that it “[defers] the callback to be executed after the next DOM update cycle.”

But Eric isn’t convinced.

He continues by narrating how he tried something like this:

this.loadingAnimation = true
this.startVeryLongCalculation()
this.completeVeryLongCalculation()
this.loadingAnimation = false

But the browser shows nothing. He heads over to Stack Overflow and someone recommends using $nextTick. This person says, “It defers the callback,” but at least they include a code snippet. Eric modifies his code to something like this:

this.loadingAnimation = true
this.$nextTick(() => this.startVeryLongCalculation())
this.endVeryLongCalculation()
this.loadingAnimation = false

It works. Why?

In this article, if you’re in a similar situation to Eric, you’re going to understand how nextTick works and see proof with a real use case.

Prerequisites

You should have basic knowledge of the following. If you don’t, review these topics:

  • Event loops
  • Callback functions
  • Microtasks, queues, and schedules
  • Async update queues

What does nextTick do?

nextTick accepts a callback function that gets delayed until the next DOM update cycle. It is just the Vue way of saying, “Hey, if you ever want to execute a function after the DOM has been updated (which happens on very rare occasions), I would prefer you use nextTick rather than setTimeout“:

Vue.nextTick(() => {}) // syntax

We will get to that setTimeout vs. nextTick argument very shortly. Let’s visualize nextTick behavior with this example:

<template>
  <div>
    {{ currentTime }}
  </div>
</template>

<script>
export default {
  name: 'getCurrentTime',
  data() {
    return {
      currentTime: ''
    }
  },
  mounted() {
    this.currentTime = 3;

    this.$nextTick(() => {
        let date = new Date()
        this.currentTime = date.getFullYear()
    });
  }
}
</script>

Note: this.$nextTick is the same as the global API method vue.nextTick, except that the this of the callback function is automatically bound to the instance that calls it.

Run this code snippet on JSFiddle or on your computer. It will display 2021. It’s not that if you remove nextTick you won’t get the same result. However, you should understand that Vue makes changes to the DOM based on what’s in the data.

In the code snippet above, Vue updates the DOM to 3, then invokes the callback, updates the DOM to 2021, and finally gives control to the browser, which displays 2021.

So far, we’ve explored the part where nextTick inserts the callback function in the callback queue and executes the function when it’s appropriate.

That’s all you need to know.

But it will interest you to know that the callback in nextTick is used as a microtask in the event loop. The source code for nextTick explicitly states that “the nextTick behavior leverages the microtask queue, which can be accessed via either native Promise.then or MutationObserver.”

setTimeout vs. nextTick

The other way to execute a function after the DOM has been updated is by using the JavaScript setTimeout() function.

Let’s replace nextTick with setTimeout in the same code example above:

<template>
  <div>
    {{ currentTime }}
  </div>
</template>

<script>
export default {
  name: 'getCurrentTime',
  data() {
    return {
      currentTime: ''
    }
  },
  mounted() {
    this.currentTime = 3;

    setTimeout(() => {
      let date = new Date()
      this.currentTime = date.getFullYear()
    }, 0);
  }
}
</script>

Run this code snippet on your local server or this JSFiddle. You will see 3 first and then 2021. It happens fast, so you might need to refresh the browser if you don’t see this behavior at first.

In the code snippet above, Vue updates the DOM to 3 and gives the browser control. Then the browser displays 3, invokes the callback, updates the DOM to 2021, and finally gives control to the browser, which now displays 2021.



The nextTick implementation uses setTimeout as the last-resort fallback method on browsers (IE 6–10 and Opera Mini browsers) where Promise and MutationObserver are not available. It even prefers setImmediate for browsers (IE 10) that don’t support Promise and MutationObserver.

The only browser that doesn’t have all three methods and has to fall back on setTimeout are Opera Mini browsers.

When to use nextTick

There are very few cases that would warrant you to pull out the big nextTick guns. Some of those cases are:

  • When you want to use setTimeout
  • When you want to be very sure that the DOM reflects your data
  • When you run into errors like Uncaught (in promise) DOMException while trying to perform asynchronous action. Remember, Vue updates the DOM asynchronously

Let’s go through one final example using Vue 3:

<div id="app">
  <div ref="listScroll" class="scrolledList">
    <ul ref="scrolledHeight">
      <li v-for="month in months">
        {{month}}
      </li>               
    </ul>
  </div>

  <input type="text" placeholder="Add Month" v-model="month">
  <button @click="addMessage" @keyup.enter="addMessage"> Add Month</button>
</div>

<script src="https://unpkg.com/[email protected]"> 
  Vue.createApp({
    data() {
      return {
        month: '',
        months: ['Jan', 'Feb', 'Apr', 'May', 'June', 'July', 'Aug']
      }
    },
    mounted() {
      this.updateScrollNextTick()
    },
    methods: {
      addMessage() {
        if(this.month == ''){
          return
        }

        this.months.push(this.month)
        this.month = ''
        this.updateScrollNextTick()
      },
      updateScrollNextTick () {
        let scrolledHeight = this.$refs.scrolledHeight.clientHeight

        this.$nextTick(() => {
          this.$refs.listScroll.scrollTo({
            behavior: 'smooth',
            top: scrolledHeight
          })
        })
      }
    },
  })
  .mount("#app")
</script>

Run this on your local machine or on CodePen. You should get something like this:

nextTick illustration

In the code snippet above, we want to get that smooth scroll-down effect when a new item is added to the list. Go through the code and experiment with modifying it by removing nextTick, and you will lose that smooth scroll effect. You can also try replacing nextTick with setTimeout.

Conclusion

In this article, we’ve explored how nextTick works. We went further to understand how it differs from the vanilla JavaScript setTimeout and covered practical use cases.

The aforementioned instance method is rarely needed, so if you’ve had to use it recently, please drop a comment under this post and share your experience.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

Chimezie Enyinnaya I'm a self-taught software developer based in Lagos, Nigeria. I enjoy teaching what I have learned and what I'm currently learning so that others can benefit from it.

Leave a Reply