Search After Stop Typing React Component


In this post I’m going to go through how to implement a search criteria component that only invokes the search in a calling component after the user has stopped typing.

So, we have the following search criteria component that invokes the search on a submit button at the moment:`// SearchCriteria.tsx

SearchCriteria.tsx
import * as React from "react";
interface SearchCriteriaProps {
placeholder: string;
doSearch: (criteria: string) => void;
}
interface SearchCriteriaState {
criteria: string;
}
class SearchCriteria extends React.Component<
SearchCriteriaProps,
SearchCriteriaState
> {
constructor() {
super();
this.state = {
criteria: ""
};
}
handleCriteriaChange = (e: React.FormEvent<HTMLInputElement>) => {
this.setState({ criteria: e.currentTarget.value });
};
handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
this.props.doSearch(this.state.criteria);
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="input-group">
<input
type="text"
placeholder={this.props.placeholder}
value={this.state.criteria}
onChange={this.handleCriteriaChange}
className="form-control"
/>
<span className="input-group-btn">
<input type="submit" value="Submit" className="btn btn-secondary" />
</span>
</div>
</form>
);
}
}
export default SearchCriteria;

… and this component is referenced like the following in a calling component:

<SearchCriteria placeholder="Enter company name ..." doSearch={this.doSearch} />

At the moment, our submit button triggers the form’s submit which in tern calls handleSubmit() which then calls this.props.doSearch in our calling component to invoke the search (our calling component actually does the search).

Here’s what our component looks like at the moment:

We want to remove the submit button and just have the search invoke when the user stops typing.

So, let’s first remove the form and submit button from our render function:

render() {
return (
<div className="input-group">
<input
type="text"
placeholder={this.props.placeholder}
value={this.state.criteria}
onChange={this.handleCriteriaChange}
className="form-control"
/>
</div>
);
}

Now let’s just invoke the search in handleCriteriaChange after we have set the criteria in the state:

handleCriteriaChange = (e: React.FormEvent<HTMLInputElement>) => {
this.setState({ criteria: e.currentTarget.value }, () => {
this.props.doSearch(this.state.criteria);
console.log(`doSearch(${this.state.criteria})`);
});
};

So, our component invokes the search on every keystroke that the user does:

Not quite what we want!

We need to “debounce” the search. That’s where the debounce function in the excellent lodash library comes in.

So, let’s bring lodash and the TypeScript types into our project:

Terminal window
npm install --save lodash
npm install --save @types/lodash

We now need to import just the debounce function into our component:

import { debounce } from "lodash";

If we look at the docs, debounce just creates another function that delays calling another function. So, let’s create our “debounce” function leaving the console.log() so that we can check that doSearch is only being called when the user has stopped typing:

raiseDoSearchWhenUserStoppedTyping = debounce(() => {
this.props.doSearch(this.state.criteria);
console.log(`doSearch(${this.state.criteria})`);
}, 300);

… and let’s call our new function from handleCriteriaChange:

handleCriteriaChange = (e: React.FormEvent<HTMLInputElement>) => {
this.setState({ criteria: e.currentTarget.value }, () => {
this.raiseDoSearchWhenUserStoppedTyping();
});
};

… and let’s give this a try: … which is just what we want!

Here’s the full code for our debouncing search criteria component:

import * as React from "react";
import { debounce } from "lodash";
interface SearchCriteriaProps {
placeholder: string;
doSearch: (criteria: string) => void;
}
interface SearchCriteriaState {
criteria: string;
}
class SearchCriteria extends React.Component<
SearchCriteriaProps,
SearchCriteriaState
> {
raiseDoSearchWhenUserStoppedTyping = debounce(() => {
this.props.doSearch(this.state.criteria);
console.log(`doSearch(${this.state.criteria})`);
}, 300);
constructor() {
super();
this.state = {
criteria: ""
};
}
handleCriteriaChange = (e: React.FormEvent<HTMLInputElement>) => {
this.setState({ criteria: e.currentTarget.value }, () => {
this.raiseDoSearchWhenUserStoppedTyping();
});
};
render() {
return (
<div className="input-group">
<input
type="text"
placeholder={this.props.placeholder}
value={this.state.criteria}
onChange={this.handleCriteriaChange}
className="form-control"
/>
</div>
);
}
}
export default SearchCriteria;

Learn React with TypeScript - 3rd Edition

New

A comprehensive guide to building modern React applications with TypeScript. Learn best practices, advanced patterns, and real-world development techniques.

View on Amazon
Learn React with TypeScript - Third Edition