Best way to throttle a "live" search input?


#1

I have a react search input that fires during onChange and stores the new value in a session value, which is passed into a subscription. The collection has 3M+ records and this is going kind of slow… it is basically re-firing the subscription on every keystroke.

I’m guessing the easiest thing to do is throttle the input a little?

Then another improvement may be somehow cancelling the subscription call if the user is still typing?


#2

Can expand this answer if required but basically: http://underscorejs.org/#debounce or https://lodash.com/docs/4.17.2#debounce depending on whether you use underscore or lodash :slight_smile:


#3

Thanks! I actually don’t use either at this point but used lodash in the past (hipster told me it isn’t cool to use underscore).

I’ll see if I can get this goin. My function is literally this basic right now:

handleTextSearch(e){
    Session.set("searchValue", {title: e.target.value}); // stores text value onChange
    Session.set('isLoading', true); // shows spinner, then reset to false once subscription is done re-running
 }

I see these:




#4

Remember that Meteor uses underscore so the _ object should be available in your Meteor code. I have a feeling it’s a fairly old version they’re running though. Underscore is a good option but you may need to a _.noConflict() statement immediately after requiring/importing it to avoid conflicting with Underscore.


#5

you think this?

import _ from 'meteor/underscore'

this isn’t doing the trick unfortunately:


 handleTextSearch(e){
    let searchString = e.target.value;
    const doSearch = () => {
      Session.set("searchValues_companies", {title: searchString});
      Session.set('isLoading', true);
    }
    _.debounce(doSearch, 500);
    
 }

#6

This worked:

import {debounce} from 'throttle-debounce';


constructor(props){
super(props)
this.doSearch = debounce(500, this.doSearch);
}
doSearch(){
       Session.set("searchValue", {title: searchString});
      Session.set('isLoading', true);
}
 handleTextSearch(e){
  this.doSearch(e.target.value);  
 }

via http://blog.revathskumar.com/2016/02/reactjs-using-debounce-in-react-components.html

thanks for you time time @mjmasn


#7

Try:


const doSearch = (searchString) => {
    Session.set("searchValues_companies", {title: searchString});
    Session.set('isLoading', true);
};

const debouncedSearch = _.debounce(doSearch, 500);

handleTextSearch(e){
    debouncedSearch(e.target.value);
}

EDIT: You beat me to it, for clarity the issue with your original code was that you were redefining the debounced function every time it was called, so it never actually debounced. Always make sure you define the debounced function outside of the function that’s calling it.


#8

Awesome, I wouldn’t have even thought of using debounce for this. I’ve been manually creating a timer via setTimeout to wait after a keystroke before searching. :thumbsup: