How to Search and Filter an array in React
Tags: react, javascript
Published: 2021-09-13 04:37:02

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:

```bash
npx create-react-app react-filter
```

Let's add some styling to our application in `index.css`:

```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:

```jsx
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:

```jsx
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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/osage0ir949t9009tv30.png)

# Searching a list of names

Let's display a search box and a list of names:

```jsx
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:

```jsx
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:

![name search](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9fyk9q08q7d70htw0pqv.png)

# Filtering a dropdown list

Update the `App.js` with the following code:

```jsx
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 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](https://www.codingdeft.com/posts/react-on-click-outside/) 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/87htdqoibz2c97xype6y.png)

# Source code and demo

You can view a demo [here](https://react-filter-lyart.vercel.app/) and the complete source code [here](https://github.com/collegewap/react-filter).