Step-by-Step Guide: Building a Simple Search Filter with React
Tags: javascript, beginners, react, tutorial
Published: 2022-07-28 18:00:00

In today's article we are going to create a basic search logic to filter the data that is being rendered in a table, list or other such component.

In the past I taught how to do a search using a similar mechanism with [debounce](https://dev.to/franciscomendes10866/how-to-create-a-search-bar-in-react-58nj), basically in the article we did the search for certain "terms" in an external api and then the search results were rendered in a list.

# Introduction

From what I've seen on the internet, the simplest and fastest approach is to filter just a small number of properties/attributes. However I find this approach very limited and many times we need to search for lots of properties.

So the idea of today's article is to have an array of objects and when we have a search term we run all these objects and all the properties of these objects to compare their values and finally return the data.

# Prerequisites

Before going further, you need:

- Node
- NPM
- React

In addition, you are expected to have basic knowledge of these technologies.

# Getting Started

## Setup Project

First let's create our project:

```sh
npm create vite@latest basic-search -- --template react
cd basic-search
```

To facilitate the creation of today's article we are going to install a library UI:

```sh
npm install @nextui-org/react
```

Now in the `main.tsx` file we add the UI library provider:

```js
// @/src/main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import { NextUIProvider } from "@nextui-org/react";

import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(
  <NextUIProvider>
    <App />
  </NextUIProvider>
);
```

The next step is to create the file with the data that we are going to work with:

```js
// @/src/assets/data.js
export const columns = [
  {
    key: "name",
    label: "NAME",
  },
  {
    key: "role",
    label: "ROLE",
  },
  {
    key: "age",
    label: "AGE",
  },
];

export const rows = [
  {
    key: "1",
    firstName: "Tony",
    lastName: "Reichert",
    role: "Developer",
    age: "35",
  },
  {
    key: "2",
    firstName: "Zoey",
    lastName: "Lang",
    role: "Designer",
    age: "22",
  },
  {
    key: "3",
    firstName: "Jane",
    lastName: "Fisher",
    role: "CEO",
    age: "29",
  },
  {
    key: "4",
    firstName: "William",
    lastName: "Howard",
    role: "Designer",
    age: "27",
  },
];
```

As you may have seen, we have two arrays in the `data.js` file in which we have the data of the columns of the table as well as the data of the rows.

With all this ready we can finally start working on the `App.jsx` where today's example will be made. First of all we need to do the following imports:

```js
// @/src/App.jsx
import React, { useMemo, useState } from "react";
import { Container, Input, Spacer, Table } from "@nextui-org/react";

import { columns, rows } from "./assets/data";

const App = () => {
  // ...
};

export default App;
```

The next step will be to create the state in which the search term will be stored:

```js
// @/src/App.jsx
import React, { useMemo, useState } from "react";
import { Container, Input, Spacer, Table } from "@nextui-org/react";

import { columns, rows } from "./assets/data";

const App = () => {
  const [searchTerm, setSearchTerm] = useState("");
  // ...
};

export default App;
```

Now we can start working on the search logic. To start we have to check if we have a search term and if we don't we will return the rows data. Then, if the rows have data, we can create the search logic.

As you well know, the search term is the value we want to use to filter the table, however we need to know which properties/attributes we want to do this search on. Similar to this:


```js
// @/src/App.jsx

// ...

const App = () => {
  const [searchTerm, setSearchTerm] = useState("");
  
  const filteredRows = useMemo(() => {
    if (!searchTerm) return rows;

    if (rows.length > 0) {
      const attributes = Object.keys(rows[0]);

      const list = [];

      // ...
      
      return list;
    }

    return [];
  }, [searchTerm, rows]);

  // ...
};

export default App;
```

Now that we have the attributes, you can now loop through each of the objects in the array (rows) and in each object we can search for the value of each of the properties/attributes.

First of all we have to ensure that we search for the value of the key, which would match the id. It's not something that's supposed to be filtered out.

If the attribute value is not undefined and it has a value similar to the search term, we can search for the correct object and then add it to the list array so that it is later returned.

```js
// @/src/App.jsx

// ...

const App = () => {
  const [searchTerm, setSearchTerm] = useState("");
  
  const filteredRows = useMemo(() => {
    if (!searchTerm) return rows;

    if (rows.length > 0) {
      const attributes = Object.keys(rows[0]);

      const list = [];

      for (const current of rows) {
        for (const attribute of attributes) {
          if (attribute === "key") {
            continue;
          }
          const value = current[attribute];
          if (value && value.toLowerCase() === searchTerm.toLowerCase()) {
            const found = rows.find((row) => row.key === current.key);
            if (found) {
              list.push(found);
            }
          }
        }
      }
      return list;
    }

    return [];
  }, [searchTerm, rows]);

  // ...
};

export default App;
```

With the logic created we can now work in our JSX, not forgetting that the data of the table rows that must be rendered is the `filteredRows`. Like this:

```js
// @/src/App.jsx
import React, { useMemo, useState } from "react";
import { Container, Input, Spacer, Table } from "@nextui-org/react";

import { columns, rows } from "./assets/data";

const App = () => {
  const [searchTerm, setSearchTerm] = useState("");

  const filteredRows = useMemo(() => {
    if (!searchTerm) return rows;

    if (rows.length > 0) {
      const attributes = Object.keys(rows[0]);

      const list = [];

      for (const current of rows) {
        for (const attribute of attributes) {
          if (attribute === "key") {
            continue;
          }
          const value = current[attribute];
          if (value && value.toLowerCase() === searchTerm.toLowerCase()) {
            const found = rows.find((row) => row.key === current.key);
            if (found) {
              list.push(found);
            }
          }
        }
      }
      return list;
    }

    return [];
  }, [searchTerm, rows]);

  return (
    <Container>
      <Spacer y={4} />
      <Input
        size="lg"
        bordered
        clearable
        placeholder="Search..."
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
      />
      <Spacer y={2} />
      <Table>
        <Table.Header>
          {columns.map((column) => (
            <Table.Column key={column.key}>{column.label}</Table.Column>
          ))}
        </Table.Header>
        <Table.Body>
          {filteredRows.map((row) => (
            <Table.Row key={row.key}>
              <Table.Cell>{row.firstName + " " + row.lastName}</Table.Cell>
              <Table.Cell>{row.role}</Table.Cell>
              <Table.Cell>{row.age}</Table.Cell>
            </Table.Row>
          ))}
        </Table.Body>
      </Table>
    </Container>
  );
};

export default App;
```

# Conclusion

As always, I hope you enjoyed this article and that it was useful to you. If you have seen any errors in the article, please let me know in the comments so that I can correct them.

Before I finish, I will share with you [this link](https://github.com/FranciscoMendes10866/nalu) to the github repository with the project code for this article.

![bye bye gif](https://i.giphy.com/media/tsX3YMWYzDPjAARfeg/giphy.webp)