How to Create a Search Bar in React

Francisco Mendes - Aug 8 '21 - - Dev Community

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:

npm i axios
Enter fullscreen mode Exit fullscreen mode

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.

// @src/api/got.js

import axios from "axios";

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

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.

// @src/hooks/useFetch.js

import { useState, useEffect } from "react";

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

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

export default useFetch;
Enter fullscreen mode Exit fullscreen mode

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.

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

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

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

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:

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

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.

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

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

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

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.

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

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

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

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

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

You should get a result similar to this:

final app

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! 👋 😜

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player