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.
.gif)
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
.gif)
Another challenge is to control the display of the list.
Requirement:
- The list should disappear on the selection
- 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;4
