DEV Community

loading...
Cover image for Project 49 of 100 - Search with Autocomplete

Project 49 of 100 - Search with Autocomplete

jwhubert91 profile image James Hubert ・3 min read

Hey! I'm on a mission to make 100 React.js projects ending March 31st. Please follow my dev.to profile or my twitter for updates and feel free to reach out if you have questions. Thanks for your support!

Link to the deployed project: Link
Link to the repo: github

Today I wanted to create an autocomplete component in React because I haven't implemented search in React before much less autocomplete. I'm actually curious how other people implement this from scratch because with the Star Wars API it's fairly easy. They have their own search feature that returns an array of JSON results and the number of Star Wars characters any search is going to return is necessarily small. What if your database has 100,000 possible results? I suppose you could put a numeric limit on the results in most databases.

For the basic search component I adapted this Dev.to blog post into a functional component. Rather than use their API, I decided to use a Star Wars character search API that is open and doesn't require a signup that exposes my email.

The structure of the website is simple. It uses an App component and a Search component, where the important logic happens. It uses three pieces of state- query, searchResults and selectedCharacter which are all set to empty at initialization:

  const [query,setQuery] = useState('');
  const [searchResults,setSearchResults] = useState([]);
  const [selectedCharacter,setSelectedCharacter] = useState(null);
Enter fullscreen mode Exit fullscreen mode

In the return statement we create a form with a text input for the search feature:

return (
    <form className='search-form-container'>
      <input 
        placeholder='Search for...'
        onChange={handleInputChange}
        value={query}
      />
    </form>
  )
Enter fullscreen mode Exit fullscreen mode

As the user searches we initiate the API call to the Star Wars API using their search URL query:

const searchURL = 'https://swapi.dev/api/people/?search=';

const getInfo = () => {
    console.log('Getting info from API...')
    fetch(searchURL+query)
      .then(res => res.json())
      .then(data => setSearchResults(data.results))
      .catch(e => {
        console.log({error: e});
    });
}

const handleInputChange = (e) => {
    setQuery(e.target.value)
    if (query && query.length > 0) {
      getInfo();
    }
}
Enter fullscreen mode Exit fullscreen mode

If results are returned from the API, we populate a ul element beneath the search box with results. I usually use the standard map method and create a key prop for the returned JSX children but I wanted to implement this a new way- with the React.Children.toArray() method. This way, you don't have to create your own key prop.

const results = React.Children.toArray(
    searchResults.map((item,idx) => (
      <li className='result-item' id={idx} onClick={handleQueryResultClick}>{item.name}</li>
    ))
)
Enter fullscreen mode Exit fullscreen mode

That looks like the following:

Alt Text

If the user selects one of these li elements, the index of that element from the original array of results stored in searchResults will match up with the id of the li element.

const handleQueryResultClick = (e) => {
    const searchResultId = e.target.id;
    setSelectedCharacter(searchResults[searchResultId]);
    setQuery([]);
}
Enter fullscreen mode Exit fullscreen mode

We then populate select data from that character's search into a div below the search box and clear the query state to remove the ul element of search results. I did this with a ternary.

<div>
        {selectedCharacter ? (
          <div className='character-display-container'>
            <p><span className='character-info-title'>name:</span> {selectedCharacter.name}</p>
            <p><span className='character-info-title'>height:</span> {selectedCharacter.height}</p>
            <p><span className='character-info-title'>mass:</span> {selectedCharacter.mass}</p>
          </div>
        ) : (
          <p className='no-results-prompt'>There are no results. Try typing something into the search bar above.</p>
        )}
</div>
Enter fullscreen mode Exit fullscreen mode

That's it! It was easier than I expected, largely because the API is so easy to use. I highly encourage you to try it.

Discussion (15)

pic
Editor guide
Collapse
jordanfinners profile image
Jordan Finneran

This is pretty cool! πŸ‘
I'd recommend checking out datalist html element for displaying the autocomplete results as it helps with accessibility and ordering results based on the input!

Collapse
jwhubert91 profile image
James Hubert Author

Thanks a bunch Jordan! I’ll definitely check out the data list html component and try to use it in a project.

Collapse
imshubh17 profile image
Shubham Singh

Quality for project also matter, not required to build 100 project

Collapse
jwhubert91 profile image
James Hubert Author

Thanks Shubham. It’s just a motivator for me- sort of a step up from #100daysofcode.
Do you think the project lacks quality? I like doing this exercise because it allows me to work on small challenges versus building whole apps.

Collapse
sudo_kaizen profile image
Orim Dominic Adah • Edited

It is awesome James!
This will help many people.
I agree with your paradigm of projects instead of code. I also agree with these simple projects because large systems are made up of a combination of them

Thread Thread
jwhubert91 profile image
James Hubert Author

Thanks Orim! It's really hard... I'm not going to lie. Sometimes I can't do a project in a day and I have to do an extra one on the weekend when I have time. But overall I think the effort it's a good strategy.

Let me know if you do a #100daysofreact or #100daysofcode! I will rep you on dev.to and on Twitter :)

Thread Thread
sudo_kaizen profile image
Orim Dominic Adah

I don't feel you MUST do a project each day. By what you've said, it's already leading to burnout. You are a human and you should take care of yourself. You are more useful to the world when you're healthy.
That being said, you can skip some days. It's #100DaysOfProjects, not #100ConsecutiveDaysOfProjects.
I'll let you know definitely if I ever start one.

Thread Thread
imshubh17 profile image
Shubham Singh

yes you are right, but for app hosting feature can be more in deployment

Thread Thread
jwhubert91 profile image
James Hubert Author

Thanks Orim. I think you're right- and please do :)

Collapse
imshubh17 profile image
Shubham Singh

Thanks james for understanding my good intend and positive reply. i saw your task, you are doing good but i want if you host app that time try to add more features so this will attractive

Collapse
ratuloss profile image
Ratul

so nice and straight forward explaination.

Collapse
jwhubert91 profile image
James Hubert Author

Thanks Ratul!

Collapse
riguidix profile image
Riguidix Rodriguez

Excellent I would try to follow all of your #100DaysOfCode projects but with Vue. Keep up the good work bro. Take care :)

Collapse
jwhubert91 profile image
James Hubert Author

I can't wait to learn Vue! It may just be next if I can learn React Native within this 100 days challenge :)

Thanks Riguidix!

Collapse
shahilsky profile image
Shahil Hussain

I am new to react. Thanx for posting the code, I can learn from it.