Autocomplete Input in React

javascript react autocomplete input data feching
Autocomplete Input in React

Overview

This is the first lab in the series of cool React mini apps/components that we are going to build together. I have tried to keep it simple and at the same time informative enough so that you folks actually learn about how to create a custom autocomplete input box in React.

Features of the component

  1. There is an input box with placeholder
  2. On focussing on the input, a list of pokemons with their name and image appear in the div below.
  3. You can type something in the input and the list will get filtered accordingly
  4. On selecting an item in the list, the list disappears with the name of the pokemon in the input.
  5. Nice to have features:
    1. If the list of pokemons is visible, clicking on the input or any where in the page, the list should disappear and should reappear again when focussing on the input

 

Learning Objectives

In this lab, you will learn about different React concepts like hooks, creating a function component, data fetching in React, usage of array functions like map, fill, filter, etc.

 

To get the most out of this series:

You need to be at least acquainted with basic JS and React concepts. You will be able to understand everything if you have given atleast a day to learn React.

 

Code/Environment Setup:

Preferred Code Editor: VS Code,

Node version >= 12.0.0

Create a react project boilerplate using the create-react-app CLI or if you have npx installed on your system you can directly do npx create-react-app

To get npx on your system, run

npm install -g npx
npx create-react-app custom-autocomplete-react
cd custom-autocomplete-react
yarn start or npm start

and there you go!

You have your react project ready to be consumed and altered into something meaningful.

Displaying the options below input

There is nothing fancy here. We have our pokemon data inside options state. Now, we will map over the options array and render it below the input (using some pretty decent css skills).

const handleOptionClick = (name) => setSearchTerm(name)

return (
  <div className="autoContainer">
    {options
      .map((value, i) => {
        return (
          <div
            onClick={() => handleOptionClick(value.name)}
            className="option"
            key={i}
          >
            <span>{value.name}</span>
            <img src={value.sprite} alt="pokemon" />
          </div>
        );
      })}
  </div>;
)

Clicking on the list item will set the searchText as the name of the pokemon clicked.

Also, we need to make the list filterable based on the searchText.

Let us change a bit there while we are iterating over the options object.

<div className="autoContainer">
  {options
    .filter(({ name }) => name.indexOf(searchText.toLowerCase()) > -1)
    .map((value, i) => {
      return (
        <div
          onClick={() => handleOptionClick(value.name)}
          className="option"
          key={i}
          tabIndex="0"
        >
          <span>{value.name}</span>
          <img src={value.sprite} alt="pokemon" />
        </div>
      );
    })}
</div>;

We are filtering the list based on the searchText (ignoring case) by using the filter method in Array.prototype.

Your component will look something like this

 

Another challenge is to control the display of the list.

Requirement:

  1. The list should disappear on the selection
  2. It should not appear until the user focusses on the input

For these, let us declare a state variable shouldDisplayOptions and initialise it to false.

We will conditionally render the options box if the state is true and will make it false on list item click. We should also make it false on clicking inside input again.

...
...
shouldDisplayOptions && (
  // iterate over and display options
)
...
...

Till this point final code looks like this:

import React, { useEffect, useState, useRef } from "react";
import "./App.css";

const AutoCompleteInput = () => {
  const [shouldDisplayOptions, setShouldDisplayOptions] = useState(false);
  const [options, setOptions] = useState([]);
  const [searchText, setSearchText] = useState("");

// fetching data for pokemons
  useEffect(() => {
    const pokemon = [];
    const promises = new Array(20)
      .fill()
      .map((v, i) => fetch(`https://pokeapi.co/api/v2/pokemon-form/${i + 1}`));
    Promise.all(promises).then(pokemonArr => {
      return pokemonArr.map(value =>
        value
          .json()
          .then(({ name, sprites: { front_default: sprite } }) =>
            pokemon.push({ name, sprite })
          )
      );
    });
    setOptions(pokemon);
  }, []);

  const handleOptionClick = poke => {
    setSearchText(poke);
    // setting shouldDisplayOptions to false
    setShouldDisplayOptions(false);
  };

  return (
    <div className="flex-container flex-column pos-rel">
      <input
        id="auto"
        onClick={() => setShouldDisplayOptions(!shouldDisplayOptions)}
        placeholder="Type to search"
        value={searchText}
        onChange={event => setSearchText(event.target.value)}
      />
      {shouldDisplayOptions && (
        <div className="autoContainer">
          {options
            .filter(({ name }) => name.indexOf(searchText.toLowerCase()) > -1)
            .map((value, i) => {
              return (
                <div
                  onClick={() => handleOptionClick(value.name)}
                  className="option"
                  key={i}
                  tabIndex="0"
                >
                  <span>{value.name}</span>
                  <img src={value.sprite} alt="pokemon" />
                </div>
              );
            })}
        </div>
      )}
    </div>
  );
};

function App() {
  return (
    <div className="App">
      <h1>Custom AutoComplete React</h1>
      <div className="auto-container">
        <AutoCompleteInput />
      </div>
    </div>
  );
}

export default App;
Discussion

3