Debouncing + React
January 02, 2022
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 propstype 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!!!