Reputation: 19150
I'm using React 16.13.0. I have this search box where people type and expect to see results as they type ...
return (
<div className="searchForm">
<input
type="text"
placeholder="Search"
value={this.state.searchTerm}
onChange={this.handleChange}
/>
I use the below functions to handle retrieving results as people type ...
handleChange(event) {
const query = event.target.value;
if (!query) {
this.setState({ searchTerm: query, searchResults: [] });
} else {
this.setState({ searchTerm: query, loading: true }, () => {
this.doSearch(query);
});
}
}
doSearch = (query) => {
const searchUrl = "/coops/?contains=" + encodeURIComponent(query);
fetch(searchUrl, {
method: "GET",
})
.then((response) => response.json())
.then((data) => {
if (query === this.state.searchTerm) {
this.setState({
searchResults: data,
loading: false,
});
}
});
};
I'm trying to cut down on the amount of network calls being made/processed. If someone types a string like "res" in a fairly rapid fashion, they probably don't care about the results for "r" or "re," so I was wondering, is there some way I can cancel a request if a new letter is entered in my search box? Or only execute the search if someone has stopped typing for at least 100ms (not sure if this is enough time, but decided to pick an arbitrary amount)?
Upvotes: 0
Views: 2140
Reputation: 21364
What you are after is usually called debounce. There is a number libraries that implement this functionality, incl. lodash and underscore. In lodash, e.g., you would do the following:
onChange={e => debouncedHandler(e, this.handleChange)}
and defined the following outside of the component:
const debouncedHandler = _.debounced((e, handler) => handler(e), 100);
It is a common pitfall to declare the debounced function inside the component itself. That will not work, because react reruns that code on every change and hence re-created the debounced function, hence the debouncing will not take place. Therefore it is important to define the debounced function outside the component.
Update: there was a mistake in the code I had earlier. It is fixed now, I think. I haven't tested this.
Upvotes: 2
Reputation: 602
You can use some helper like this and pass the calling function as the first argument callback
.
function getDoneTyping (callback, wait = 300) {
let timer
const keyDownHandler = event => {
if (event.key === 'Enter') {
callback()
}
clearTimeout(timer)
}
const keyUpHandler = event => {
clearTimeout(timer)
timer = setTimeout(callback, wait)
}
return { keyDownHandler, keyUpHandler }
}
Upvotes: 0
Reputation: 14191
You can delay the searching via setTimeout
. Just clear the timeout whenever a new input is caught. If timer expires, go and do the request
handleChange = (e) => {
this.setState(
{
searchTerm: e.target.value
},
() => {
try {
clearInterval(window.xhrSearchTimer);
} catch (e) {
console.log(`no timeout initialized`);
}
window.xhrSearchTimer = window.setTimeout(() => {
this.doSearch(this.state.searchTerm);
}, 1000); // arbitrary value
}
);
};
doSearch = () => {
alert(`Now searching for ${this.state.searchTerm}`);
};
https://codesandbox.io/s/loving-sid-hb0nr?file=/src/App.js:139-622
Upvotes: 0