Debouncing + React

January 02, 2022

JavaScript
TypeScript
React

Debouncing is a technique to control how many times we allow a function to be executed over time. For today's article, we’ll reference a search bar since it's a common use case for this scenario when your'e trying to manage a large data set containing thousands of nodes across your server(s).

The lodash docs give a great definition:

...function that delays invoking func until after wait ms have elapsed since the last time the denounced function was invoked


Use Case

In the Fullcourt app, we currently use debounce to restrict the number of calls we make to our server. Imagine having a list of Cities and with every event change we not only shake our tree, but we’re making accessive calls to query a city/or cities, and that can cause bad performance for our users.


Approach

We can create a custom hook and import it into our search module. The question is do you really need to do this? Yes! Yes, you do if you want to save money on your car insurance by switching to... I’m joking lol. Seriously, yes.

Our custom hook will look like this useDebounced<T>(f: func, w: number, o?: Object) f take in the function and w will allow our hook to be configurable, and o will be our options.

Let’s Implement it below:

// for our options that contains flags
// lets iterate through the keys to create a type
// we don't use but I figured it be fun to make this type
type OptionsFlags<T> = {
[Property in keyof T]: boolean
}
type Flags = {
leading?: boolean
trailing?: boolean
}
type DebounceFlags = OptionsFlags<Flags>
type MaxWaitOption<T> = {
[key: keyof T]: number
}
type MaxWaitProp = {
maxWait?: number
}
type MaxWait = MaxWaitOptions<MaxWaitProp>
// intersect our props
type Options = DebounceFlags & MaxWait
interface DebounceProps = {
func: () => void
wait: number
options?: Options
}
function useDebounced({ func, wait }: DebounceProps) {
let [debouncedValue, setDebouncedValue] = useState(func)
useEffect(() => {
let timeOutId = setTimeout(() => {
setDebouncedValue(func)
}, wait)
// cleanup
return () => {
clearTimeout(timeoutId)
}
}, [func, wait])
return debouncedValue
}
module.exports = { useDebounced }

Usage

Now we can just drop it into our Search Component and call it a day. One thing you will notice is that we’re no longer causing a bottleneck to our server, and no matter how many characters we type into our search the useDebounced hook will only fire wait ms after the user is done typing.

I’m only implementing pseudo logic for our use case. This will give you a general idea of how to add this piece to your puzzle

import React from 'react';
import { useDebounced } from '../src/hooks/';
const Example: React.FC = ({}) => {
const [query, setQuery] = useState < string > '';
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e?.target?.value);
};
// we call our custom hook :)
const debouncedOnChange = useDebounced(onChange, 500);
{
/* semantic HTML FTW!!! */
}
return (
<div class="search-container">
<form action="#" method="get">
<label forHTML={query}>
Search
{/* use it on our onChange prop */}
<input
type="text"
name={query}
placeholder="Search for a City..."
onChange={debouncedOnChange}
/>
</label>
{/*other fun stuff below*/}
</form>
</div>
);
};

Conclusion

Today we learned about debouncing and the problems it can resolve for many users who are experiencing performance issues. Debouncing is a common technique for handling events in a sophisticated way. We can target events to help us avoid doing extra work and allow us to control when we want to execute our function.

Resources:

I hope you enjoyed reading the article :) Happy New Year!!!


my shoe
I’m Marlon but you can call me Mars. Software Engineer. Music lover. Bay Area Native. Feel free to Contact me.
  • implement promisify