Debouncing events with React

Anupama
2 min readFeb 3, 2019

--

I recently came across a use case in the project I am working on where I had to debounce the input onChange event callback.

A common use case is when you have an input element like below which functions as a search input in your app.

<input name="search-input" onChange={this.onChange} />

If you are making API calls to fetch the search data as shown below, then, to make it performant you should not make the API call for every character that is input by the user, but only after the user input stops for some milliseconds.

onChange = (event) => {
let searchString = event.target.value;
fetchSearchData(searchString);
}

This is easily achieved by using a debounce function. Below I am using the debounce function from the lodash library:

<input onChange={_.debounce(this.onChange, 300)} />

The above code passes a debounced function as the event callback to the onChange event. The _.debounce function ensures that the actual onChange event callback is called only when the user has stopped inputting the characters for 300ms.

But doing this in a React application throws the following error:

Warning: This synthetic event is reused for performance reasons. If you’re seeing this, you’re accessing the property `target` on a released/nullified synthetic event.

Error in the console

This error is thrown because React does event pooling. This means that the event object (which is a wrapper created by React over the actual event object) that is passed to an event callback is reused and hence it will be nullified or cleared once the event callback finishes. So accessing event.target.value in the example above throws an error because event object was nullified when the event callback finished and we are trying to access it later (after 300ms in this example) through the debounce function.

To fix this issue, we need to tell React that the event object will be used in the future and hence should not be nullified. This is done by calling the persist() method on React’s synthetic event object.

So we have to rewrite the onChange event handler as shown below to make it work with debounce:

onChange = (event) => {
/* signal to React not to nullify the event object */
event.persist();

if (!this.debouncedFn) {
this.debouncedFn = _.debounce(() => {
let searchString = event.target.value;
fetchSearchData(searchString);
}, 300);
}
this.debouncedFn();
}

// in render method
<input name="search-input" onChange={this.onChange} />

That’s it folks! Happy Reacting :-)

--

--