

Debouncing with React Hooks
source link: https://nick.scialli.me/debouncing-with-react-hooks/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Debouncing with React Hooks
Nick Scialli • April 09, 2019 • 🚀🚀 6 minute read
In this post, I am going to use the React Hooks API to implement debouncing. If you are unfamiliar with debouncing, it is simply a mechanism to postpose the execution of a function. Typically, this will be implemented in javascript to prevent a potentially costly operation (e.g., requesting data from the backend) from being executed prematurely. An example of debouncing in real life is a typeahead list that only executes a search some amount of time after a user stops typing.
Setup
I will use an example in which we filter a JSON file of US states based on user input. We don’t want to filter the list until the user stops typing, so we will want to implement debouncing. Our JSON file will be in the following format:
[
{
"name": "Alabama",
"abbreviation": "AL"
},
{
"name": "Alaska",
"abbreviation": "AK"
},
...
]
For simplicity, we’ll just filter the name
field based on user input.
Implementing filtering hooks without debouncing
Let’s quickly implement the functionality without debouncing. This means the list of states will be filtered automatically as the user types.
Note: This is not an introduction to hooks post; if the following code seems entirely foreign to you, you may want to read the React Hooks Introuction documentation.
import React, { useState, useEffect, Fragment } from 'react';
import states from './states';
const App = () => {
const [search, setSearch] = useState('');
const [filteredStates, setFilteredStates] = useState(states);
useEffect(() => {
const filter = states.filter(state => {
return state.name.toLowerCase().includes(search.toLowerCase());
});
setFilteredStates(filter);
}, [search]);
return (
<Fragment>
<input value={search} onChange={e => setSearch(e.target.value)} />
<ul>
{filteredStates &&
filteredStates.map(state => {
return <li key={state.abbreviation}>{state.name}</li>;
})}
</ul>
</Fragment>
);
};
export default App;
When our input element is changes, setSearch
is called, which changes our search
value, re-rendering the component. The useEffect
hook runs as we have indicated it should whenever search
changes. Within the useEffect
hook, we filter states
based on the search
value.
If we run our application at this point, we see it functions as desired.
Add debouncing
To add debouncing, we can intuit that the useEffect
hook should be updated with a timer. Let’s add a setTimeout
with a 1 second wait and see how that works.
useEffect(() => {
setTimeout(() => {
const filter = states.filter(state => {
return state.name.toLowerCase().includes(search.toLowerCase());
});
setFilteredStates(filter);
}, 1000);
}, [search]);
This isn’t quite right. It does delay the filtering, but it doesn’t wait until you stop typing. Essentially, the app now just waits a second and starts catching up to what you’re typing. This is not the ideal behavior–the exact same amount of filtering is happening.
There is a simple fix: the useEffect
hook offers a “cleanup” mechanism that will run when the component re-renders next. All we have to do is return the cleanup function from first function passed to the useEffect
hook. In our example, we simple need to cancel the setTimeout
. If the 1 second has not passed before the user types more, the setTimeout
will be canceled and the filter will not be applied.
useEffect(() => {
const timer = setTimeout(() => {
const filter = states.filter(state => {
return state.name.toLowerCase().includes(search.toLowerCase());
});
setFilteredStates(filter);
}, 1000);
return () => clearTimeout(timer);
}, [search]);
Success! We now wait until 1 second after the last user input to filter the states. The full code can be viewed below.
App.js
import React, { useState, useEffect, Fragment } from 'react';
import states from './states';
const App = () => {
console.log('render');
const [search, setSearch] = useState('');
const [filteredStates, setFilteredStates] = useState(states);
useEffect(() => {
const timer = setTimeout(() => {
const filter = states.filter(state => {
return state.name.toLowerCase().includes(search.toLowerCase());
});
setFilteredStates(filter);
}, 1000);
return () => clearTimeout(timer);
}, [search]);
return (
<Fragment>
<input value={search} onChange={e => setSearch(e.target.value)} />
<ul>
{filteredStates &&
filteredStates.map(state => {
return <li key={state.abbreviation}>{state.name}</li>;
})}
</ul>
</Fragment>
);
};
export default App;
states.json
[
{
"name": "Alabama",
"abbreviation": "AL"
},
{
"name": "Alaska",
"abbreviation": "AK"
},
{
"name": "American Samoa",
"abbreviation": "AS"
},
{
"name": "Arizona",
"abbreviation": "AZ"
},
{
"name": "Arkansas",
"abbreviation": "AR"
},
{
"name": "California",
"abbreviation": "CA"
},
{
"name": "Colorado",
"abbreviation": "CO"
},
{
"name": "Connecticut",
"abbreviation": "CT"
},
{
"name": "Delaware",
"abbreviation": "DE"
},
{
"name": "District Of Columbia",
"abbreviation": "DC"
},
{
"name": "Federated States Of Micronesia",
"abbreviation": "FM"
},
{
"name": "Florida",
"abbreviation": "FL"
},
{
"name": "Georgia",
"abbreviation": "GA"
},
{
"name": "Guam",
"abbreviation": "GU"
},
{
"name": "Hawaii",
"abbreviation": "HI"
},
{
"name": "Idaho",
"abbreviation": "ID"
},
{
"name": "Illinois",
"abbreviation": "IL"
},
{
"name": "Indiana",
"abbreviation": "IN"
},
{
"name": "Iowa",
"abbreviation": "IA"
},
{
"name": "Kansas",
"abbreviation": "KS"
},
{
"name": "Kentucky",
"abbreviation": "KY"
},
{
"name": "Louisiana",
"abbreviation": "LA"
},
{
"name": "Maine",
"abbreviation": "ME"
},
{
"name": "Marshall Islands",
"abbreviation": "MH"
},
{
"name": "Maryland",
"abbreviation": "MD"
},
{
"name": "Massachusetts",
"abbreviation": "MA"
},
{
"name": "Michigan",
"abbreviation": "MI"
},
{
"name": "Minnesota",
"abbreviation": "MN"
},
{
"name": "Mississippi",
"abbreviation": "MS"
},
{
"name": "Missouri",
"abbreviation": "MO"
},
{
"name": "Montana",
"abbreviation": "MT"
},
{
"name": "Nebraska",
"abbreviation": "NE"
},
{
"name": "Nevada",
"abbreviation": "NV"
},
{
"name": "New Hampshire",
"abbreviation": "NH"
},
{
"name": "New Jersey",
"abbreviation": "NJ"
},
{
"name": "New Mexico",
"abbreviation": "NM"
},
{
"name": "New York",
"abbreviation": "NY"
},
{
"name": "North Carolina",
"abbreviation": "NC"
},
{
"name": "North Dakota",
"abbreviation": "ND"
},
{
"name": "Northern Mariana Islands",
"abbreviation": "MP"
},
{
"name": "Ohio",
"abbreviation": "OH"
},
{
"name": "Oklahoma",
"abbreviation": "OK"
},
{
"name": "Oregon",
"abbreviation": "OR"
},
{
"name": "Palau",
"abbreviation": "PW"
},
{
"name": "Pennsylvania",
"abbreviation": "PA"
},
{
"name": "Puerto Rico",
"abbreviation": "PR"
},
{
"name": "Rhode Island",
"abbreviation": "RI"
},
{
"name": "South Carolina",
"abbreviation": "SC"
},
{
"name": "South Dakota",
"abbreviation": "SD"
},
{
"name": "Tennessee",
"abbreviation": "TN"
},
{
"name": "Texas",
"abbreviation": "TX"
},
{
"name": "Utah",
"abbreviation": "UT"
},
{
"name": "Vermont",
"abbreviation": "VT"
},
{
"name": "Virgin Islands",
"abbreviation": "VI"
},
{
"name": "Virginia",
"abbreviation": "VA"
},
{
"name": "Washington",
"abbreviation": "WA"
},
{
"name": "West Virginia",
"abbreviation": "WV"
},
{
"name": "Wisconsin",
"abbreviation": "WI"
},
{
"name": "Wyoming",
"abbreviation": "WY"
}
]
Recommend
-
41
Hooks are a new feature proposal that lets you use state and other React features without writing a class. They’re currently in React v16.7.0-alpha and being discussed in
-
44
W hen building an app using React, we always have this problem of limiting the number of expensive calls, async network requests and DOM updates. We can really check all these using the features provided...
-
27
Introduction A common conundrum in today’s front-end framework world is knowing when and how to take certain asynchronous actions, such as persisting data to a backend. If we’re using a state management library l...
-
7
-
7
Improve Your App Performance with Event Debouncing May 21 2018 | By Taha Shashtari If you have a text input that fires an AJAX request each time the user types something into it, it would be inefficient not...
-
8
Tutorial Throttling and Debouncing Events with Vue.js and lodash Vue.js ...
-
6
Skip to content Checkout my Getting Started with React video course on Skillshare. It is 100% free....
-
4
What is debouncing in JavaScript? While building an app using...
-
12
In many cases, you want to limit how often a function is called. Implementing such limits can lead to complex code, unless a functional technique is used. In this article, we’ll show how we can debounce and throttle functions (don’t worry! we’ll e...
-
3
@maratMaratHi guys, I'm Marat, a software developer with expertise i...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK