140
votes

I read the docs, but I can't understand it. I know what data, computed, watch, methods do but what is nextTick() use for in vuejs?

4
The key concept to understand is that the DOM is updated asynchronously. When you change a value in Vue, the change is not immediately rendered to the DOM. Instead, Vue queues a DOM update and then, on a timer, updates the DOM. Normally, this happens so fast that it doesn't make a difference, but, at times, you need to update the rendered DOM after Vue has rendered it, which you can't immediately do in a method because the update hasn't happened yet. In those cases, you would use nextTick. Documented here.Bert
Complementing what @Bert said in https://stackoverflow.com/q/47634258/9979046 above, the nextTick() will be used in Unit Tests, when you need to check if a element exists in DOM (HTML), for example, if you get some information on a Axios request.Oscar Alencar
why do I feel like nextTick is something like const nextTick = (callback, context) => { setTimeout(callback.bind(context), 0); }; ?SparK

4 Answers

187
votes

nextTick allows you to do something after you have changed the data and VueJS has updated the DOM based on your data change, but before the browser has rendered those changed on the page.

Normally, devs use native JavaScript function setTimeout to achieve similar behavior. But, using setTimeoutrelinquishes control over to the browser before it gives control back to you via your callback.

Let's say, you changed some data. Vue updates DOM based on the data. Mind you the DOM changes are not yet rendered to the screen by the browser. If you used nextTick, your callback gets called now. Then, browser updates the page. If you used setTimeout, your callback would get called only now.

You can visualize this behavior by creating a small component like the following:

<template>
  <div class="hello">
    {{ msg }}
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
        msg: 'One'
    }
  },
  mounted() {
      this.msg = 'Two';

      this.$nextTick(() => {
          this.msg = 'Three';
      });
  }
}
</script>

Run your local server. You will see the message Three being displayed.

Now, replace your this.$nextTick with setTimeout

setTimeout(() => {
    this.msg = 'Three';
}, 0);

Reload the browser. You will see Two, before you see Three.

Check this fiddle to see it live

That's because, Vue updated the DOM to Two, gave control to the browser. Browser displayed Two. Then, called your callback. Vue updated the DOM to Three. Which the browser displayed again.

With nextTick. Vue udpated the DOM to Two. Called your callback. Vue updated the DOM to Three. Then gave control to the browser. And, the browser displayed Three.

Hope it was clear.

To understand how Vue implements this, you need to understand the concept of Event Loop and microtasks.

Once you have those concepts clear(er), check the source code for nextTick.

17
votes

Next Tick basically allows you to run some code, after the vue has re-rendered the component when you have made some changes to the a reactive property (data).

// modify data
vm.msg = 'Hello'
// DOM not updated yet
Vue.nextTick(function () {
   // this function is called when vue has re-rendered the component.
})
    
// usage as a promise (2.1.0+, see note below)
Vue.nextTick()
   .then(function () {
       // this function is called when vue has re-rendered the component.
    })

From the Vue.js Documentation:

Defer the callback to be executed after the next DOM update cycle. Use it immediately after you’ve changed some data to wait for the DOM update.

Read more about it, here.

10
votes

To make Pranshat's answer about the difference between using nextTick and setTimeout, more explicit, I have forked his fiddle: here

mounted() {    
  this.one = "One";
     
  setTimeout(() => {
    this.two = "Two"
  }, 0);
      
  //this.$nextTick(()=>{
  //  this.two = "Two"
  //})}
}

You can see in the fiddle that when using setTimeOut, the initial data flashes very briefly once the component gets mounted before adapting the change. Whereas, when using nextTick, the data is hijacked, changed, before rendering to the browser. So, the browser shows the updated data without even any knowledge of the old. Hope that clears the two concepts in one swoop.

2
votes

I have created a useful demo in what scenario we can use nextTick in Vuejs, if you want update or run something immediately after your DOM updates, see addMessage function where i am calling another function in which i am using a nextTick function to update scroll to see latest message.

<!DOCTYPE html>
<html>
    <head>
        <title>CDN VUE 3</title>
    </head>
    <body>
        <div id="app">
        <div ref="scrolledList" style="height: 100px; width: 150px; border:1px solid red; overflow: auto; margin-bottom: 15px; padding: 5px;">
           <ul ref="scrolledHeight" style="margin: 0; padding: 0;">
               <li v-for="msg in messages">
                   {{msg}}
               </li>               
           </ul>
        </div>
           <input type="text" placeholder="Add Message" v-model="message" />
           <button @click="addMessage" @keyup.enter="addMessage"> Add Message</button>
        </div>
        <script src="https://unpkg.com/vue@next"></script>
        <script>
            Vue.createApp({
                data() {
                    return {
                       message: '',
                       messages: [1,2,3,4,5,6,7,8,9,10]
                    }
                },
                mounted() {
                    this.updateScrollNextTick()
                },
                methods: {
                    addMessage() {
                        if(this.message == ''){
                            return
                        }
                        this.messages.push(this.message)
                        this.message = ""
                        this.updateScrollNextTick()
                    },
                    updateScrollNextTick () {
                        this.$nextTick( () => {
                            let scrolledHeight = this.$refs.scrolledHeight.clientHeight
                            this.$refs.scrolledList.scrollTo({
                                behavior: 'smooth',
                                top: scrolledHeight
                            })
                        })
                    }
                },
            })
            .mount("#app")
        </script>
    </body>
</html>