Table of Contents
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.
If you are looking for filtering array of objects, you can refer this article.
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:
1npx create-react-app react-filter
Let's add some styling to our application in index.css
:
1body {2 margin: 10px auto;3 max-width: 700px;4}56body ::-webkit-scrollbar-track {7 background: rgba(0, 0, 0, 0.1);8 border-radius: 0;9}10body ::-webkit-scrollbar-thumb {11 cursor: pointer;12 border-radius: 5px;13 background: rgba(0, 0, 0, 0.25);14 -webkit-transition: color 0.2s ease;15 transition: color 0.2s ease;16}17body ::-webkit-scrollbar {18 -webkit-appearance: none;19 width: 10px;20 height: 10px;21}22label {23 margin-left: 5px;24 margin-right: 5px;25}26.dropdown-search {27 border: solid 1px black;28 display: inline-block;29 padding: 1px 2px;30 border-radius: 2px;31 cursor: pointer;32 width: 180px;33 font-size: 13.3333px;34}35.default {36 border: solid 1px grey;37 color: grey;38}39.dropdown-input {40 width: 180px;41 display: block;42}4344.dropdown-list ul {45 border: 1px solid gray;46 margin: 0;47 padding: 0;48 display: inline-block;49 width: 186px;50 max-height: 200px;51 overflow-y: scroll;52 border-top: none;53}54.dropdown-list li {55 list-style: none;56 padding: 5px;57 cursor: pointer;58}5960.dropdown-list li:hover {61 background: rgba(0, 0, 0, 0.03);62 font-weight: bold;63}64.dropdown-wrapper {65 display: inline-block;66}6768.dropdown-list li.no-result {69 color: grey;70}7172.dropdown-list li.no-result:hover {73 font-weight: normal;74}
Filtering numbers
Update App.js
with the following code:
1import { useState } from "react"23let numbers = [64, 84, 22, 32, 83, 65, 51, 26, 23, 56]4function App() {5 const [filteredNumbers, setFilteredNumbers] = useState(numbers)67 return (8 <div className="App">9 <h2>Number filtering</h2>10 <input type="radio" name="evenOrOdd" id="even" value="even" />11 <label htmlFor="even">Even</label>12 <input type="radio" name="evenOrOdd" id="odd" value="odd" />13 <label htmlFor="odd">Odd</label>14 <ul>15 {filteredNumbers.map(number => {16 return <li key={number}>{number} </li>17 })}18 </ul>19 </div>20 )21}2223export 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:
1import { useState } from "react"23let numbers = [64, 84, 22, 32, 83, 65, 51, 26, 23, 56]4function App() {5 const [filteredNumbers, setFilteredNumbers] = useState(numbers)67 const radioChangeHandler = e => {8 const value = e.target.value9 if (value === "even") {10 setFilteredNumbers(11 numbers.filter(number => {12 if (number % 2 === 0) {13 return true14 }15 return false16 })17 )18 } else {19 setFilteredNumbers(20 numbers.filter(number => {21 if (number % 2 !== 0) {22 return true23 }24 return false25 })26 )27 }28 }2930 return (31 <div className="App">32 <h2>Number filtering</h2>33 <input34 type="radio"35 name="evenOrOdd"36 id="even"37 value="even"38 onChange={radioChangeHandler}39 />40 <label htmlFor="even">Even</label>41 <input42 type="radio"43 name="evenOrOdd"44 id="odd"45 value="odd"46 onChange={radioChangeHandler}47 />48 <label htmlFor="odd">Odd</label>49 <ul>50 {filteredNumbers.map(number => {51 return <li key={number}>{number} </li>52 })}53 </ul>54 </div>55 )56}5758export 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:
1import { useState } from "react"23let names = [4 "Shea",5 "Ewing",6 "Yang",7 "Mcintosh",8 "Castillo",9 "Cunningham",10 "Johnston",11 "Mckay",12 "Roberson",13 "Perez",14 "Dudley",15 "Wood",16]17function App() {18 const [searchValue, setSearchValue] = useState("")1920 return (21 <div className="App">22 <h2>Search filtering</h2>23 <input24 type="text"25 name="search"26 value={searchValue}27 onChange={e => setSearchValue(e.target.value)}28 />29 <ul>30 {names.map(name => {31 return <li key={name}>{name} </li>32 })}33 </ul>34 </div>35 )36}3738export default App
We have the value of the input box stored in the local state: searchValue
.
Let's use it to filter the list:
1import { useState } from "react"23let names = [4 "Shea",5 "Ewing",6 "Yang",7 "Mcintosh",8 "Castillo",9 "Cunningham",10 "Johnston",11 "Mckay",12 "Roberson",13 "Perez",14 "Dudley",15 "Wood",16]17function App() {18 const [searchValue, setSearchValue] = useState("")1920 return (21 <div className="App">22 <h2>Search filtering</h2>23 <input24 type="text"25 name="search"26 value={searchValue}27 onChange={e => setSearchValue(e.target.value)}28 />29 <ul>30 {names31 .filter(name => name.match(new RegExp(searchValue, "i")))32 .map(name => {33 return <li key={name}>{name} </li>34 })}35 </ul>36 </div>37 )38}3940export 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:
1import { useEffect, useRef, useState } from "react"23let skills = [4 "Angular",5 "CSS",6 "Graphic Design",7 "Ember",8 "HTML",9 "Information Architecture",10 "JavaScript",11 "Mechanical Engineering",12 "Meteor",13 "NodeJS",14 "Plumbing",15 "Python",16 "Rails",17 "React",18 "Kitchen Repair",19 "Ruby",20 "UI Design",21 "User Experience",22]2324function App() {25 const [selectedSkill, setSelectedSkill] = useState("")26 const [dropdownSearchValue, setDropdownSearchValue] = useState("")27 const [editMode, setEditMode] = useState(false)28 const dropdownRef = useRef()2930 /**31 * Close the dropdown when clicked outside32 * Refer https://www.codingdeft.com/posts/react-on-click-outside/ for details33 */34 useEffect(() => {35 const checkIfClickedOutside = e => {36 // If the menu is open and the clicked target is not within the menu,37 // then close the menu38 if (39 editMode &&40 dropdownRef.current &&41 !dropdownRef.current.contains(e.target)42 ) {43 setEditMode(false)44 }45 }46 document.addEventListener("mousedown", checkIfClickedOutside)47 return () => {48 // Cleanup the event listener49 document.removeEventListener("mousedown", checkIfClickedOutside)50 }51 }, [editMode])5253 const skillSelectionHandler = skill => {54 setSelectedSkill(skill)55 setDropdownSearchValue("")56 setEditMode(false)57 }5859 const filteredSkills = skills.filter(skill =>60 skill.match(new RegExp(dropdownSearchValue, "i"))61 )6263 return (64 <div className="App">65 <h2>Dropdown filtering</h2>6667 {editMode ? (68 // display the dropdown when the input us focused69 <div ref={dropdownRef} className="dropdown-wrapper">70 <input71 className="dropdown-input"72 name="dropdown-input"73 autoFocus74 onChange={e => setDropdownSearchValue(e.target.value)}75 value={dropdownSearchValue}76 />77 <div className="dropdown-list">78 <ul>79 {filteredSkills.map(skill => {80 return (81 <li key={skill} onClick={() => skillSelectionHandler(skill)}>82 {skill}{" "}83 </li>84 )85 })}86 {filteredSkills.length === 0 && (87 <li className="no-result">No results found</li>88 )}89 </ul>90 </div>91 </div>92 ) : (93 <input94 // Grey out the text when "Select Primary skill" input hint is shown95 className={`dropdown-search ${96 !(dropdownSearchValue || selectedSkill) && "default"97 }`}98 onFocus={() => setEditMode(true)}99 // Display the selected skill or "Select Primary skill" input hint100 value={selectedSkill || "Select Primary skill"}101 />102 )}103 </div>104 )105}106107export 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:
Source code and demo
You can view a demo here and the complete source code here.
Do follow me on twitter where I post developer insights more often!