46

I'm using lodash to call a debounce function on a component like so:

...
import _ from 'lodash';

export default {
    store,
    data: () => {
        return {
            foo: "",
        }
    },

    watch: {
        searchStr: _.debounce(this.default.methods.checkSearchStr(str), 100)
    },

    methods: {
        checkSearchStr(string) {
            console.log(this.foo) // <-- ISSUE 1
            console.log(this.$store.dispatch('someMethod',string) // <-- ISSUE 2
        }
    }
}
  • Issue 1 is that my method checkSearchStr doesn't know about foo
  • Issue 2 is that my store is undefined as well

Why doesn't my method know this when called through _.debounce? And what is the correct usage?

Artur Grigio
  • 5,185
  • 8
  • 45
  • 65

4 Answers4

81

Your watch should look like this.

watch: {
    searchStr: _.debounce(function(newVal){
      this.checkSearchStr(newVal)
    }, 100)
},

This is a bit unusual, however. I don't see why you would want to debounce a watch. Possibly you would rather just debounce the checkSearchStr method.

watch: {
    searchStr(newVal){
      this.checkSearchStr(newVal)
    }
},

methods: {
    checkSearchStr: _.debounce(function(string) {
        console.log(this.foo) 
        console.log(this.$store.dispatch('someMethod',string)) 
    }, 100)
}

One other thing I would like to point out; no where in the code is searchStr defined. When you watch a value with Vue, you are watching a data or computed property. As you have currently defined it, the watch on searchStr will never execute.

Bert
  • 80,741
  • 17
  • 199
  • 164
  • 9
    I think that the `this` (this.foo) inside the function isnt the VueJS. – rogeriolino Jul 18 '17 at 23:37
  • 1
    @rogeriolino Thats not correct. When a component is instantiated, the method is bound to the component. In other words, `this` *will* be the Vue. – Bert Jul 18 '17 at 23:38
  • 1
    I just found the Vue.js documentation for `debounce` (docs - https://vuejs.org/v2/guide/migration.html#debounce-Param-Attribute-for-v-model-removed). Thanks for your answer (part 2), it was very helpful. I should not be running `debounce` on the watched element. – Artur Grigio Jul 18 '17 at 23:56
  • 1
    hmm.. I think the code can be shorter: watch: { searchStr: "checkSearchStr" } source: https://www.youtube.com/watch?v=7YZ5DwlLSt8&feature=youtu.be&t=4m35s – Hiep May 16 '18 at 16:45
  • 1
    @Hiep Sure, that is documented in the API. https://vuejs.org/v2/api/#watch – Bert May 16 '18 at 17:36
  • Just make the 'fetch data' function debounce. Cuz thats where u wanna take the load of. – Cas Bloem Aug 28 '18 at 14:50
5

All the examples shown in the answers and in the Vue documentation are actually not very good because all instances of your component will share a single debounce method. This means that if you have 2 instances of the component on a single page and they both fire the debounced method within the 100ms window only 1 of the 2 components will work.

Now in most scenarios this is probably fine since it's a more niche issue but if you do run into this issue it's safer to create the debounce methods within your components created() so that it's instance specific.

  created() {
    this.$watch('checkSearchStr', _.debounce(function(string) {
      console.log(this.foo)
      console.log(this.$store.dispatch('someMethod',string))
    }, 100));
  }
Zaptree
  • 3,763
  • 1
  • 31
  • 26
  • I like your solution, but I think its better to do smth like that ` this.unwatch = this.$watch(....)` and to call it in `beforeDestroy` – Levy_from_Odessa Dec 07 '22 at 14:20
  • 1
    @Levy_from_Odessa that is not required as per the documentation: "Watchers declared using the watch option or the $watch() instance method are automatically stopped when the owner component is unmounted, so in most cases you don't need to worry about stopping the watcher yourself." – Zaptree Feb 02 '23 at 21:59
  • @Zaptree There should be an arrow for correct this. unwatch is not required but flush/cancel is advisable because debounced function operates on nonexistent instance – Estus Flask Mar 29 '23 at 09:22
0

The right way to use debounce while being able to use this inside the function:

watch: {
    searchStr(newVal){
      this.checkSearchStr(this, newVal)
    }
},

methods: {
    checkSearchStr: _.debounce(function(self, newVal) {
        console.log(self.foo) 
        console.log(self.$store.dispatch('someMethod',newVal)) 
    }, 100)
}
chickens
  • 19,976
  • 6
  • 58
  • 55
-1

As @Bert mentioned in comments this scope is local to the function. Therefore, to make this scope to properties in data, change to:

methods: {
    checkSearchStr: _.debounce((string) => {
        console.log(this.foo) 
        console.log(this.$store.dispatch('someMethod',string)) 
    }, 100)
}
Adam Cox
  • 3,341
  • 1
  • 36
  • 46