Tulamthings

How to debounce an autocomplete input in React

You can see that many websites usually suggest somethings for us as soon as we typing in the search box or some kind of form.

It's an essential feature for a website to improve the experience of users when they use our application. We can build that feature by using an autocomplete input.

Let imagine that your users want to borrow some books from your library. And they have to fill the form with the list of books they wanted. You will likely create a select input for your user to choose from instead of forcing them to type entirely the book's title.

But showing all the books from your library in the select input or fired an API call every single time they type something is not a smart way to do that. So we need an autocomplete field that showing a shortlist of books and whenever people have done typing, it fires an API call to the backend and displays the result as a suggestion.

In this article, I'll implement an autocomplete input field with the react-select library as well as using the debounce function to prevent unnecessary calls to API.

Create an autocomplete input with react-select

First things first, we need to create a react project and install all dependencies needed.

Installation & Usage

Make sure your computer already installed

  • yarn/npm
  • create react app CLI

We'll create a new react project by typing

bash
npx create-react-app select-input-debounce

After that is done, we install the react-select package from npm/yarn

bash
yarn add react-select

That is all we need for preparation. Next, we'll implement an autocomplete input

Usage

In App.js file. Import AsyncSelect from react-select/async package and stateOptions which is the dump data we have been prepared

jsx
// App.js
import AsyncSelect from 'react-select/async'
import { stateOptions } from '../seeds'

Which those are imported, we are able to use the AsyncSelect component in our app and have data to display for users.

Next, we implement a function to filter the result when users type something in the input field

jsx
// App.js
// import AsyncSelect and stateOptions
const filterStates = (inputValue) => {
return stateOptions.filter((state) => state.label.toLowerCase().includes(inputValue.toLowerCase()))
}

Then, use the AsyncSelect component to render an input in our App.js component

jsx
// App.js
return (
<div className='select-input'>
<AsyncSelect loadOptions={loadOptions} cacheOptions defaultOptions={loadDefaultOptions(stateOptions)} />
</div>
)
  • loadOptions props accept a resolve from a callback or promise and will display the option for the input field
  • defaultOptions accept an option array that will populate an initial set of options used when opening the select input, at which point the remote load only occurs when filtering the options (typing in the control)
  • cacheOptions helps the loaded data be cached. The cache will remain until cacheOptions changes values

We are not finished yet. Next, we have to define a function that will fire whenever users typing something in the select box.

jsx
const loadOptions = (inputValue) => {
return new Promise((resolve) => {
setTimeout(() => resolve(filterStates(inputValue)), 1000)
})
}

We use the setTimeout() function here to mimic the behavior that it takes time to fetch data from the server.

We have finished the autocomplete input. But have you notice that it fires the filterStates() function whenever users type a single character.

In a real-world project. It means that it fires a call to API whenever the input value has changed. And it wasted resource and we want to improve that

Using debounce function to prevent unnecessary API call

What is a debounce function?

Basically, it's a function that forces another function to wait until a certain amount of time until it calls again. We use it to limit the amount of time a function is called. In this example is the time calling filterStates function.

In the App.js file, declare an timeoutId variable in global scope and define the debounce function like below

jsx
let timeoutId
const debounce = (func, delay) => {
return function () {
if (timeoutId) {
clearTimeout(timeoutId)
}
timeoutId = setTimeout(() => {
func()
}, delay)
}
}
  • func is the function that we want to limit
  • delay is the amount of time we'd like the function will be delay

Whenever use type a character. They will trigger an event and that event will call to debounce function.

Then we check for the value of timeoutId variable. If it's undefined, we set the value for timeoutId with the return value of the setTimeout() function. If the delay time is passed, timeoutId will return to be undefined and the *func will be called.

But if the debounce function is called again before the delay time has passed. It means that timeoutId already has a value. We have to clear that value and assign a new value for timeoutId variable to refresh it.

The last step we have made is changing the loadOptions function to called debounce function whenever the input is changed like this

jsx
const loadOptions = (inputValue) => {
return new Promise((resolve) => {
debounce(() => resolve(filterStates(inputValue)), 500)()
})
}

In here, we will call the filterStates() function each 500ms after the last time users type something.

We have made a simple autocomplete input with a debounce function in React. The source code for this example is below. Take a look if you want.

Full example code is here <--

Share this post