Search code examples
javascriptvue.jsvuejs2debouncing

Vuejs 2: debounce not working on a watch option


When I debounce this function in VueJs it works fine if I provide the number of milliseconds as a primitive. However, if I provide it as a reference to a prop, it ignores it.

Here's the abbreviated version of the props:

props : {
    debounce : {
        type : Number,
        default : 500
    }
}

Here is the watch option that does NOT work:

watch : {
    term : _.debounce(function () {
        console.log('Debounced term: ' + this.term);
    }, this.debounce)
}

Here is a watch option that DOES work:

watch : {
    term : _.debounce(function () {
        console.log('Debounced term: ' + this.term);
    }, 500)
}

It suspect that it is a scope issue but I don't know how to fix it. If I replace the watch method as follows...:

watch : {
    term : function () {
        console.log(this.debounce);
    }
}

... I get the correct debounce value (500) appearing in the console.


Solution

  • The primary issue here is using this.debounce as the interval when defining your debounced function. At the time _.debounce(...) is run (when the component is being compiled) the function is not yet attached to the Vue, so this is not the Vue and this.debounce will be undefined. That being the case, you will need to define the watch after the component instance has been created. Vue gives you the ability to do that using $watch.

    I would recommend you add it in the created lifecycle handler.

    created(){
      this.unwatch = this.$watch('term', _.debounce((newVal) => {
         console.log('Debounced term: ' + this.term);
      }, this.debounce))
    },
    beforeDestroy(){
      this.unwatch()
    }
    

    Note above that the code also calls unwatch which before the component is destroyed. This is typically handled for you by Vue, but because the code is adding the watch manually, the code also needs to manage removing the watch. Of course, you will need to add unwatch as a data property.

    Here is a working example.

    console.clear()
    
    Vue.component("debounce",{
      props : {
        debounce : {
          type : Number,
          default : 500
        }
      },
      template:`
        <input type="text" v-model="term">
      `,
      data(){
        return {
          unwatch: null,
          term: ""
        }
      },
      created(){
        this.unwatch = this.$watch('term', _.debounce((newVal) => {
          console.log('Debounced term: ' + this.term);
        }, this.debounce))
      },
      beforeDestroy(){
        this.unwatch()
      }
    })
    
    new Vue({
      el: "#app"
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
    <script src="https://unpkg.com/vue@2.4.2"></script>
    <div id="app">
      <debounce :debounce="250"></debounce>
    </div>