DEV Community

Cover image for How to Search and Filter an array in React
collegewap
collegewap

Posted on • Originally published at codingdeft.com

How to Search and Filter an array in React

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
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

number filter

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

name search

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
Enter fullscreen mode Exit fullscreen mode

In the above code:

  • We have an array of skills.
  • By default, editMode state will be set to false, 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 to true 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 to filteredSkills 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 the skillSelectionHandler function and closing the dropdown.

If you run the application now, you should be able to search and choose a skill:

dropdown search

Source code and demo

You can view a demo here and the complete source code here.

Discussion (0)