June 23, 2021
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 StackOverflow 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.

[H2] 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
[H2] 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.”

[H3] se“tTimeout 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 3 methods and has to fall back on setTimeout are Opera Mini browsers.

[H2] 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/vue@next”>
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:

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.

[H2] Conclusion

In this article, we’ve explored how nextTick works. We went further to understand how it differs from 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.

The post Understanding <code>$nextTick</code> in Vue.js appeared first on LogRocket Blog.

Leave a Reply

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

Send