How to implement debounce in Vue2?
Solution 1
I am using debounce NPM package and implemented like this:
<input @input="debounceInput">
methods: {
debounceInput: debounce(function (e) {
this.$store.dispatch('updateInput', e.target.value)
}, config.debouncers.default)
}
Using lodash and the example in the question, the implementation looks like this:
<input v-on:input="debounceInput">
methods: {
debounceInput: _.debounce(function (e) {
this.filterKey = e.target.value;
}, 500)
}
Solution 2
Option 1: Re-usable, no deps
- Recommended if needed more than once in your project
/helpers.js
export function debounce (fn, delay) {
var timeoutID = null
return function () {
clearTimeout(timeoutID)
var args = arguments
var that = this
timeoutID = setTimeout(function () {
fn.apply(that, args)
}, delay)
}
}
/Component.vue
<script>
import {debounce} from './helpers'
export default {
data () {
return {
input: '',
debouncedInput: ''
}
},
watch: {
input: debounce(function (newVal) {
this.debouncedInput = newVal
}, 500)
}
}
</script>
Option 2: In-component, also no deps
- Recommended if using once or in small project
/Component.vue
<template>
<input type="text" v-model="input" />
</template>
<script>
export default {
data: {
timeout: null,
debouncedInput: ''
},
computed: {
input: {
get() {
return this.debouncedInput
},
set(val) {
if (this.timeout) clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.debouncedInput = val
}, 300)
}
}
}
}
</script>
Solution 3
Assigning debounce in methods
can be trouble. So instead of this:
// Bad
methods: {
foo: _.debounce(function(){}, 1000)
}
You may try:
// Good
created () {
this.foo = _.debounce(function(){}, 1000);
}
It becomes an issue if you have multiple instances of a component - similar to the way data
should be a function that returns an object. Each instance needs its own debounce function if they are supposed to act independently.
Here's an example of the problem:
Vue.component('counter', {
template: '<div>{{ i }}</div>',
data: function(){
return { i: 0 };
},
methods: {
// DON'T DO THIS
increment: _.debounce(function(){
this.i += 1;
}, 1000)
}
});
new Vue({
el: '#app',
mounted () {
this.$refs.counter1.increment();
this.$refs.counter2.increment();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
<div id="app">
<div>Both should change from 0 to 1:</div>
<counter ref="counter1"></counter>
<counter ref="counter2"></counter>
</div>
Solution 4
Very simple without lodash
handleScroll: function() {
if (this.timeout)
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
// your action
}, 200); // delay
}
Solution 5
I had the same problem and here is a solution that works without plugins.
Since <input v-model="xxxx">
is exactly the same as
<input
v-bind:value="xxxx"
v-on:input="xxxx = $event.target.value"
>
I figured I could set a debounce function on the assigning of xxxx in xxxx = $event.target.value
like this
<input
v-bind:value="xxxx"
v-on:input="debounceSearch($event.target.value)"
>
methods:
debounceSearch(val){
if(search_timeout) clearTimeout(search_timeout);
var that=this;
search_timeout = setTimeout(function() {
that.xxxx = val;
}, 400);
},
Related videos on Youtube
MartinTeeVarga
Studied Computer Graphics - Computational Geometry. Currently living and working in Sydney. Java, JavaScript, TDD. Book author and hobby game programmer: Mr. Dandelion's Adventures, and blogging about Android Game Development as well (with occasional game review). Running to keep fit, travelling the world and learning Japanese.
Updated on November 17, 2021Comments
-
MartinTeeVarga over 2 years
I have a simple input box in a Vue template and I would like to use debounce more or less like this:
<input type="text" v-model="filterKey" debounce="500">
However the
debounce
property has been deprecated in Vue 2. The recommendation only says: "use v-on:input + 3rd party debounce function".How do you correctly implement it?
I've tried to implement it using lodash, v-on:input and v-model, but I am wondering if it is possible to do without the extra variable.
In template:
<input type="text" v-on:input="debounceInput" v-model="searchInput">
In script:
data: function () { return { searchInput: '', filterKey: '' } }, methods: { debounceInput: _.debounce(function () { this.filterKey = this.searchInput; }, 500) }
The filterkey is then used later in
computed
props.-
sobolevn over 7 yearsTry this one stackoverflow.com/questions/41230343/…
-
Marek Urbanowicz over 7 yearsI would suggest to carefully read: vuejs.org/v2/guide/…
-
Bengt over 7 yearsThere is an example in the guide: vuejs.org/v2/guide/computed.html#Watchers
-
-
MartinTeeVarga over 7 yearsThanks for this. I found a similar example in some other Vue docs: vuejs.org/v2/examples/index.html (the markdown editor)
-
mix3d about 7 yearsWouldn't this call
debounceInput()
twice for each change?v-on:
will detect the input changes and call debounce, AND because the model is bound, searchInput's watch function will ALSO calldebounceInput
... right? -
MartinTeeVarga about 7 years@mix3d Do not consider this answer. It was just my investigation I did not want to put in the question. You are most likely right. Check the accepted answer. It's correct and I edited it to match the question.
-
mix3d about 7 yearsMy mistake... I didn't realize you had answered your own question, ha!
-
Valera over 6 yearsProposed solution has a problem when there are several component instances on the page. Problem is described and solution presented here: forum.vuejs.org/t/issues-with-vuejs-component-and-debounce/7224/…
-
MartinTeeVarga about 6 yearsCould you explain why assigning debounce in methods can be trouble?
-
MartinTeeVarga about 6 yearsSee Example links are prone to link-rot. It's better to explain the problem in the answer - it will make it more valuable for the readers.
-
Admin almost 6 yearsThank you very match, i had a bad time trying to understand why the data displayed on the console was right but not applied on the app ...
-
ness-EE almost 6 yearse.currentTarget is overwritten to null this way
-
Mike Sheward over 5 years@sm4 because instead of using the same shared debounced instance for your desired function, it recreates it each time, thus killing the use of debounce mainly.
-
Matt Komarnicki over 5 yearsThanks, you helped me a lot with my vue.js component…
-
Flame over 5 yearsThanks I was looking for this answer. Apparently when you make a
_.debounce
of the same function in copies of a component, only the last created component will be called. -
DominikAngerer over 5 yearsWould recommend to add a
v-model=your_input_variable
to the input and in your vuedata
. So you do not rely one.target
but use Vue so you can accessthis.your_input_variable
instead ofe.target.value
-
Suau over 5 yearsjust add it to your
data()
then. -
Neon22 about 5 yearsif your input field also had an
@input="update_something"
action then call this afterthat.xxx = val
that.update_something();
-
Neon22 about 5 yearsin my methods section I used a slightly different syntax which worked for me:
debounceSearch: function(val) { if (this.search_timeout) clearTimeout(this.search_timeout); var that=this; this.search_timeout = setTimeout(function() { that.thread_count = val; that.update_something(); }, 500); },
-
Coreus almost 5 yearsThis is ok if you're having one or very few instances where you need to debounce input. However, you'll quickly realize you'll need to move this to a library or similar if the app grows and this functionality is needed elsewhere. Keep your code DRY.
-
Ashtonian almost 5 yearsyou the real hero
-
Kshitiz over 4 yearsJust wasted an hour debugging this!
-
Ben Winding over 4 yearsI prefer this option because I probably don't need an npm package for 11 lines of code....
-
Alexander Kludt over 4 yearsThis should be the marked answer, this works really well and takes nearly no space at all. Thanks!
-
Michael Hays over 4 yearsAs much as I love lodash, this is clearly the best answer for a trailing debounce. Easiest to implement as well as understand.
-
Polosson over 4 yearsFor those using ES6, it's important to emphasize the use of the anonymous function here: if you use an arrow function you will not be able to access
this
within the function. -
Hybrid web dev about 4 yearsthis is actually wrong. You don't need to declare methods in lifecycle hooks as they are linked to their own instance. Not sure where OP got this idea from, but yeah he's 100% wrong.
-
Barney Szabolcs about 4 yearsProbably this one should be the accepted solution, with 100+ vote-ups. The OP asked for a compact solution like this, and it nicely decouples the debounce logic.
-
Barney Szabolcs about 4 yearsMy vote got locked in. The best solution so far is actually the one with 5 vote-ups: stackoverflow.com/a/50347709/1031191 because it neatly decouples the debounce logic from the container.
-
Barney Szabolcs about 4 yearsno. OP asked for a vue html property, as is best for this. See: stackoverflow.com/a/50347709/1031191 (not my answer, that is the cleanest so far, even though it has only 5 vote-ups. It deserves a lot more, I think...)
-
pikilon about 4 yearsalso is a good thing to add
destroyed() { clearInterval(this.timeout) }
in order to not having a timeout after destroyed. -
nunop about 4 yearsCould you please add more information about this solution?
-
jpnadas about 4 yearsPlease elaborate a little bit more. Also, note that this is an old thread with well established answers, so can you clarify how your solution is more appropriate for the problem?
-
PJP about 4 yearsIt helps more if you supply an explanation why this is the preferred solution and explain how it works. We want to educate, not just provide code.
-
Jordash over 3 yearsOut of all the solutions this is the only one that worked reliably.
-
thiebo about 3 yearsSimple, efficient, great !
-
ness-EE about 3 years@Hybridwebdev I reckon he got it from Linus Borg's answer from the Vue forum, so I would say that this is the correct solution forum.vuejs.org/t/…
-
flourigh about 3 yearshi, possible add a TypeScript Version o helper?
-
Luis Cabrera Benito almost 3 yearsThank you! This way I can access
this
inside the debounced function -
SimonHawesome almost 3 yearsis any one else getting a jest error when implementing the first option?
[Vue warn]: Error in callback for watcher "input": "TypeError: Cannot read property 'call' of undefined"
-
Bcktr almost 3 yearsIt will be so hard if you play with array, because this way is depends with the static data
-
Tom T over 2 years@Polosson Why does it need to be an anonymous function? Seems like an arrow function would let you get the correct
this
. I've implemented the solution above exactly with an arrow function and without, and I'm running intothis
issues in both cases. Not sure what's wrong. -
Just a coder over 2 yearsi am not sure how to use this when text changes on an input field. Can someone show an example?
-
brad over 2 years@Justacoder you need to add an event listener to the input. Google
input addEventListener
-
Gery Ruslandi over 2 yearsi just encountered this case. you are 3 years ahead of me haha. thankyou random people