You might have come across many applications where you can search a list of items or a long dropdown list within which you can search for the item easily. In this tutorial, we will see how to achieve this in React.
We will be implementing the following:
- Filter a list of numbers based on if they are even or odd.
- Search from a list of names.
- A dropdown to choose a primary skill, with a search option.
Setting up the project
Create a new react app using the following command:
npx create-react-app react-filter
Let's add some styling to our application in index.css
:
body {
margin: 10px auto;
max-width: 700px;
}
body ::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 0;
}
body ::-webkit-scrollbar-thumb {
cursor: pointer;
border-radius: 5px;
background: rgba(0, 0, 0, 0.25);
-webkit-transition: color 0.2s ease;
transition: color 0.2s ease;
}
body ::-webkit-scrollbar {
-webkit-appearance: none;
width: 10px;
height: 10px;
}
label {
margin-left: 5px;
margin-right: 5px;
}
.dropdown-search {
border: solid 1px black;
display: inline-block;
padding: 1px 2px;
border-radius: 2px;
cursor: pointer;
width: 180px;
font-size: 13.3333px;
}
.default {
border: solid 1px grey;
color: grey;
}
.dropdown-input {
width: 180px;
display: block;
}
.dropdown-list ul {
border: 1px solid gray;
margin: 0;
padding: 0;
display: inline-block;
width: 186px;
max-height: 200px;
overflow-y: scroll;
border-top: none;
}
.dropdown-list li {
list-style: none;
padding: 5px;
cursor: pointer;
}
.dropdown-list li:hover {
background: rgba(0, 0, 0, 0.03);
font-weight: bold;
}
.dropdown-wrapper {
display: inline-block;
}
.dropdown-list li.no-result {
color: grey;
}
.dropdown-list li.no-result:hover {
font-weight: normal;
}
Filtering numbers
Update App.js
with the following code:
import { useState } from "react"
let numbers = [64, 84, 22, 32, 83, 65, 51, 26, 23, 56]
function App() {
const [filteredNumbers, setFilteredNumbers] = useState(numbers)
return (
<div className="App">
<h2>Number filtering</h2>
<input type="radio" name="evenOrOdd" id="even" value="even" />
<label htmlFor="even">Even</label>
<input type="radio" name="evenOrOdd" id="odd" value="odd" />
<label htmlFor="odd">Odd</label>
<ul>
{filteredNumbers.map(number => {
return <li key={number}>{number} </li>
})}
</ul>
</div>
)
}
export default App
In the above code, we have added a couple of radio buttons to choose between odd and even numbers and we have a state called filteredNumbers
, which we are initializing with the list of numbers. We are looping through the numbers and displaying them in a list.
Let's add the filtering logic to our code:
import { useState } from "react"
let numbers = [64, 84, 22, 32, 83, 65, 51, 26, 23, 56]
function App() {
const [filteredNumbers, setFilteredNumbers] = useState(numbers)
const radioChangeHandler = e => {
const value = e.target.value
if (value === "even") {
setFilteredNumbers(
numbers.filter(number => {
if (number % 2 === 0) {
return true
}
return false
})
)
} else {
setFilteredNumbers(
numbers.filter(number => {
if (number % 2 !== 0) {
return true
}
return false
})
)
}
}
return (
<div className="App">
<h2>Number filtering</h2>
<input
type="radio"
name="evenOrOdd"
id="even"
value="even"
onChange={radioChangeHandler}
/>
<label htmlFor="even">Even</label>
<input
type="radio"
name="evenOrOdd"
id="odd"
value="odd"
onChange={radioChangeHandler}
/>
<label htmlFor="odd">Odd</label>
<ul>
{filteredNumbers.map(number => {
return <li key={number}>{number} </li>
})}
</ul>
</div>
)
}
export default App
Here, we have written a function called radioChangeHandler
, in which we check if the selected radio box value is "even". If it is "even", then we call the JavaScript filter function and
use the modulus operator (%) to determine if the number is even. The array returned (array of even numbers) from the filter function is then set to the filteredNumbers
state. A similar logic is written in the else condition for odd numbers.
If you run the application now, you should be able to filter the odd and even numbers:
Searching a list of names
Let's display a search box and a list of names:
import { useState } from "react"
let names = [
"Shea",
"Ewing",
"Yang",
"Mcintosh",
"Castillo",
"Cunningham",
"Johnston",
"Mckay",
"Roberson",
"Perez",
"Dudley",
"Wood",
]
function App() {
const [searchValue, setSearchValue] = useState("")
return (
<div className="App">
<h2>Search filtering</h2>
<input
type="text"
name="search"
value={searchValue}
onChange={e => setSearchValue(e.target.value)}
/>
<ul>
{names.map(name => {
return <li key={name}>{name} </li>
})}
</ul>
</div>
)
}
export default App
We have the value of the input box stored in the local state: searchValue
.
Let's use it to filter the list:
import { useState } from "react"
let names = [
"Shea",
"Ewing",
"Yang",
"Mcintosh",
"Castillo",
"Cunningham",
"Johnston",
"Mckay",
"Roberson",
"Perez",
"Dudley",
"Wood",
]
function App() {
const [searchValue, setSearchValue] = useState("")
return (
<div className="App">
<h2>Search filtering</h2>
<input
type="text"
name="search"
value={searchValue}
onChange={e => setSearchValue(e.target.value)}
/>
<ul>
{names
.filter(name => name.match(new RegExp(searchValue, "i")))
.map(name => {
return <li key={name}>{name} </li>
})}
</ul>
</div>
)
}
export default App
Here we are making a case insensitive match for the name.
If you run the application now, you should be able to search and filter the name:
Filtering a dropdown list
Update the App.js
with the following code:
import { useEffect, useRef, useState } from "react"
let skills = [
"Angular",
"CSS",
"Graphic Design",
"Ember",
"HTML",
"Information Architecture",
"Javascript",
"Mechanical Engineering",
"Meteor",
"NodeJS",
"Plumbing",
"Python",
"Rails",
"React",
"Kitchen Repair",
"Ruby",
"UI Design",
"User Experience",
]
function App() {
const [selectedSkill, setSelectedSkill] = useState("")
const [dropdownSearchValue, setDropdownSearchValue] = useState("")
const [editMode, setEditMode] = useState(false)
const dropdownRef = useRef()
/**
* Close the dropdown when clicked outside
* Refer https://www.codingdeft.com/posts/react-on-click-outside/ for details
*/
useEffect(() => {
const checkIfClickedOutside = e => {
// If the menu is open and the clicked target is not within the menu,
// then close the menu
if (
editMode &&
dropdownRef.current &&
!dropdownRef.current.contains(e.target)
) {
setEditMode(false)
}
}
document.addEventListener("mousedown", checkIfClickedOutside)
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside)
}
}, [editMode])
const skillSelectionHandler = skill => {
setSelectedSkill(skill)
setDropdownSearchValue("")
setEditMode(false)
}
const filteredSkills = skills.filter(skill =>
skill.match(new RegExp(dropdownSearchValue, "i"))
)
return (
<div className="App">
<h2>Dropdown filtering</h2>
{editMode ? (
// display the dropdown when the input us focused
<div ref={dropdownRef} className="dropdown-wrapper">
<input
className="dropdown-input"
name="dropdown-input"
autoFocus
onChange={e => setDropdownSearchValue(e.target.value)}
value={dropdownSearchValue}
/>
<div className="dropdown-list">
<ul>
{filteredSkills.map(skill => {
return (
<li key={skill} onClick={() => skillSelectionHandler(skill)}>
{skill}{" "}
</li>
)
})}
{filteredSkills.length === 0 && (
<li className="no-result">No results found</li>
)}
</ul>
</div>
</div>
) : (
<input
// Grey out the text when "Select Primary skill" input hint is shown
className={`dropdown-search ${
!(dropdownSearchValue || selectedSkill) && "default"
}`}
onFocus={() => setEditMode(true)}
// Display the selected skill or "Select Primary skill" input hint
value={selectedSkill || "Select Primary skill"}
/>
)}
</div>
)
}
export default App
In the above code:
- We have an array of skills.
- By default,
editMode
state will be set tofalse
, and an input box with greyed out text "Select Primary skill" will be displayed. - When the user clicks/focuses on the text box,
editMode
will be set totrue
and a dropdown will be displayed with the list of skills. - When the user types something on the search box,
dropdownSearchValue
will be updated with the keyword. The skills will be filtered and set tofilteredSkills
and displayed. - We also have a
useEffect
hook to close the dropdown whenever the user clicks outside the dropdown. I have written a detailed article on handling outside click in React components earlier. - When the user clicks on any of the skills, we are setting it to
selectedSkill
state in theskillSelectionHandler
function and closing the dropdown.
If you run the application now, you should be able to search and choose a skill:
Top comments (0)