How to Create a Search Bar in React
Tags: javascript, node, react, beginners
Published: 2021-08-08 13:24:01

One of the things I was currently interested in was creating a search bar, however I didn't want to search for things that were already available in the frontend. In the same way that I didn't feel like making a request to an Api by clicking on a button.

I just wanted to use an Input and as soon as I finish writing, I would automatically make the request to the Api and this is where the challenge of today's example lies.

The idea of today's example is to write the name of a house from Game of Thrones and then we will list the family name and its members.

# Let's code

In today's example we'll just install axios to make http requests for our application:

```sh
npm i axios
```

The application scaffold is up to you because it will work anyway, whether you use `create-react-app`, `vite` or other.

First we will create the axios instance and then consume the [Game Of Thrones Quotes API](https://gameofthronesquotes.xyz/).

```js
// @src/api/got.js

import axios from "axios";

export default axios.create({
  baseURL: "https://game-of-thrones-quotes.herokuapp.com/v1/house",
});
```

Then we can start working on our custom hook. For this to work we are going to use two well known hooks that you are probably familiar with, `useState()` and `useEffect()`. Just like we're going to import our axios instance.

```js
// @src/hooks/useFetch.js

import { useState, useEffect } from "react";

import got from "../api/got";

const useFetch = () => {
  // ...
  return;
};

export default useFetch;
```

Let's start by creating our state, which in this example will be an object with two properties. We have the `slug`, which will be the search term, this being the name of the house. And the second will be the `results` of Api's response.

```js
// @src/hooks/useFetch.js

import { useState, useEffect } from "react";

import got from "../api/got";

const useFetch = () => {
  const [data, setData] = useState({
    slug: "",
    results: [],
  });
  // ...
  return;
};

export default useFetch;
```

Now we can use `useEffect()`, which will be executed whenever the slug is changed. This way:

```js
// @src/hooks/useFetch.js

import { useState, useEffect } from "react";

import got from "../api/got";

const useFetch = () => {
  const [data, setData] = useState({
    slug: "",
    results: [],
  });

  useEffect(() => {
    // ...
  }, [data.slug]);

  return;
};

export default useFetch;
```

However, as you might have already thought. If we now make the http request inside `useEffect()`, it means that whenever the user writes a new character, he will make a new request to the Api.

But we don't want that, so we'll use `setTimeout()`, because we want the http request to be done as soon as the user finishes writing, for that we'll have a timeout of one second.

But then we will be taking a risk, we could end up having several timeouts and we don't want that. This is because if the timeout starts and the user writes again, we want to cancel the previous timeout. So we need to clean up our `useEffect()` and we will use `clearTimeout()` to cancel the previous timeout. Like this:


```js
// @src/hooks/useFetch.js

import { useState, useEffect } from "react";

import got from "../api/got";

const useFetch = () => {
  const [data, setData] = useState({
    slug: "",
    results: [],
  });

  useEffect(() => {
    if (data.slug !== "") {
      const timeoutId = setTimeout(() => {
        // ...
      }, 1000);
      return () => clearTimeout(timeoutId);
    }
  }, [data.slug]);

  return;
};

export default useFetch;
```

Now we can make our http request using our axios instance and let's pass our `slug` as the only parameter. We will then store the response data in our state.

```js
// @src/hooks/useFetch.js

import { useState, useEffect } from "react";

import got from "../api/got";

const useFetch = () => {
  const [data, setData] = useState({
    slug: "",
    results: [],
  });

  useEffect(() => {
    if (data.slug !== "") {
      const timeoutId = setTimeout(() => {
        const fetch = async () => {
          try {
            const res = await got.get(`/${data.slug}`);
            setData({ ...data, results: res.data });
          } catch (err) {
            console.error(err);
          }
        };
        fetch();
      }, 1000);
      return () => clearTimeout(timeoutId);
    }
  }, [data.slug]);

  return;
};

export default useFetch;
```

Now just return our state and our setter to be able to use our custom hook.

```js
// @src/hooks/useFetch.js

import { useState, useEffect } from "react";

import got from "../api/got";

const useFetch = () => {
  const [data, setData] = useState({
    slug: "",
    results: [],
  });

  useEffect(() => {
    if (data.slug !== "") {
      const timeoutId = setTimeout(() => {
        const fetch = async () => {
          try {
            const res = await got.get(`/${data.slug}`);
            setData({ ...data, results: res.data });
          } catch (err) {
            console.error(err);
          }
        };
        fetch();
      }, 1000);
      return () => clearTimeout(timeoutId);
    }
  }, [data.slug]);

  return { data, setData };
};

export default useFetch;
```

Now we can start by creating our UI, first let's start with our `App.jsx`. Likewise, we'll import our custom hook and import the `House.jsx` component (which hasn't been created yet) and we'll do conditional rendering, as we'll only show house data if we have some.

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

import React from "react";

import useFetch from "./hooks/useFetch";
import House from "./components/House";

export default function App() {
  const { data, setData } = useFetch();
  return (
    <main>
      <input
        type="text"
        placeholder="Type your favorite house"
        value={data.slug}
        onChange={(e) => setData({ ...data, slug: e.target.value })}
      />
      <br />
      {data.results.length > 0 ? <House family={data.results[0]} /> : null}
    </main>
  );
}
```

Now we can start creating our `House.jsx` component, and in the same we will import `Members.jsx` (which has yet to be created).

```js
// @src/components/House.jsx

import React from "react";

import Members from "./Members";

export default function House({ family }) {
  return (
    <div>
      <h1>{family.name}</h1>
      <Members members={family.members} />
    </div>
  );
}
```

Finally we can create our last component that will list each of the family members.

```js
// @src/components/Members.jsx

import React from "react";

export default function Members({ members }) {
  return (
    <ul>
      {members.map((el, i) => (
        <li key={i}>{el.name}</li>
      ))}
    </ul>
  );
}
```

You should get a result similar to this:

![final app](https://res.cloudinary.com/dj5iihhqv/image/upload/v1628428758/Kapture_2021-08-08_at_14.18.12-min_jazegk.gif)

## Conclusion

As always, I hope you found it interesting. If you noticed any errors in this article, please mention them in the comments. 🪗

Hope you have a great day! 👋 😜