Search Filter for List
Develop a component that filters items in a list based on a text input search query.
Solution
Type your local borough
- City of London
- Barking and Dagenham
- Barnet
- Bexley
- Brent
- Bromley
- Camden
- Croydon
- Ealing
- Enfield
- Greenwich
- Hackney
- Hammersmith and Fulham
- Haringey
- Harrow
- Havering
- Hillingdon
- Hounslow
- Islington
- Kensington and Chelsea
- Kingston upon Thames
- Lambeth
- Lewisham
- Merton
- Newham
- Redbridge
- Richmond upon Thames
- Southwark
- Sutton
- Tower Hamlets
- Waltham Forest
- Wandsworth
- Westminster
Explanation
The HTML Structure
The component is made up of an input
and an unordered list
where the list
items are mapped over.
handleInput function
When the input is updated, the onChange
event calls the handleInput
function.
searchInput
stores the current value of the input. Which is in turn filtered to return those values included in the list and stored in updatedList
.
Debounce
Although not essential for this action, a debounce
function is used to ensure that the list seen by the user isn't updated too quickly to become distracting.
The debounce
function is often used to prevent too many API calls.
How Debounce works
The function takes a callback
and a delay
.
A timer
is referenced at this level of scope. An anonymous function is returned that clears any current timer and sets a new one.
Now when the function is called before the delay time has passed, that callback is removed and a new delay is initiated.
Code
import { CSSProperties, useState } from "react";
import styles from "./Solution.module.css";
const londonBoroughs = [
"City of London",
"Barking and Dagenham",
...
"Wandsworth",
"Westminster",
];
const Solution = () => {
const [filteredList, setFilteredList] = useState<string[]>(londonBoroughs);
const debouncedSetFilteredList = debounceSetList((newList: string[]) => {
setFilteredList(newList);
});
const handleInput: React.ChangeEventHandler<HTMLInputElement> = (event) => {
const searchInput = event?.target.value;
const updatedList = londonBoroughs.filter((boro) =>
boro.toLowerCase().includes(searchInput.toLowerCase())
);
debouncedSetFilteredList(updatedList);
};
return (
<div>
<h2>Type your local borough</h2>
<input onChange={handleInput} type="text" className={styles.input} />
<ul className={styles.list}>
{filteredList.map((boro) => (
<li className={styles.li} key={boro}>
{boro}
</li>
))}
</ul>
</div>
);
};
export default Solution;
// debounce function
function debounceSetList(
// callback here is referencing setFilteredList
// takes a string array and returns nothing
callback: (newList: string[]) => void,
delay: number = 500
): (newList: string[]) => void {
let timer: number | null = null;
return (newList: string[]) => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
callback(newList);
}, delay) as unknown as number;
};
}
Styling
.input {
padding: 5px;
margin: 30px 0;
}
.list {
display: flex;
flex-wrap: wrap;
width: 500px;
gap: 5px;
}
.li {
padding: 3px;
border: 1px solid grey;
border-radius: 5px;
}