How to build a React Autocomplete component

  • 2019-04-09 01:10 AM
  • 88

In this article you will learn how to build an autocomplete component in React using only local state and some simple event handlers.

You can get the autocomplete search function, like the one you see in a Google search, in a React app. I’ll call this application React Autocomplete.

The search recommendation in Google comes from their servers. This React component will take an array of recommendations as props. For the demo, I’ll just pass an array to this component. But you can also get recommendation array from an AJAX request and pass it as the props the exact same way.

Here’s the project structure using create-react-app.

app-structure

Autocomplete.js renders the Autocomplete component, and App.js is the App component that renders Autocomplete component. These two components make this React Autocomplete application possible.

This is the initial App.js. Autocomplete component needs suggestions.

import React, { Component } from 'react';
import './App.css';
import Autocomplete from './Autocomplete';
class App extends Component {
  render() {
    return (
      <div className="App">
        <Autocomplete
          suggestions={['White', 'Black', 'Green', 'Blue', 'Yellow', 'Red']}
        />
      </div>
    );
  }
}
export default App;

Here’s the Autocomplete component itself. It doesn’t make use of propTypes or suggestions [received as props]. We’ll fix that in a while.

import React, { Component } from 'react';
import PropTypes from 'prop-types';

export class Autocomplete extends Component {
  static propTypes = {};

  render() {
    return <div>Hello from AutoComplete</div>;
  }
}
export default Autocomplete;

Verify that the application is working before moving forward…

initial app

That doesn’t look great, but it’s working!

Define the constructor in Autocomplete to set state.

constructor(props) {
    super(props);
    this.state = {
      activeSuggestion: 0,
      filteredSuggestions: [],
      showSuggestions: false,
      userInput: ''
    };
  }

Autocomplete component will use these states to suggest if the input phrase matches.

Also, define defaultProperty with suggestions as an empty array.

static defaultProperty={
        suggestions: []
      };

The Autocomplete component renders an input and a list of suggestions, these are all wrapped in a React.Fragment. It is a container, just like div, but it doesn’t render itself in the final output. This avoids unnecessary wrapping divs.

render(){
   return (
      <React.Fragment>
        <input
          type="text"
          onChange={onChange}
          onKeyDown={onKeyDown}
          value={userInput}
        />
        {suggestionsListComponent}
      </React.Fragment>
    );
};

The component suggestionsListComponent is an unordered list of suggestions.

Define onChange, onKeyDown and value to make it run as expected. onChange and onKeyDown methods modify the states activeSuggestion, filteredSuggestions, showSuggestions, userInput.

Suggestions List Component

Use suggestionsListComponent to display the list, keep it in the Autocomplete (inside render function) and get the states from Autocomplete.

let suggestionsListComponent;
    if (showSuggestions && userInput) {
      if (filteredSuggestions.length) {
        suggestionsListComponent = (
          <ul class="suggestions">
            {filteredSuggestions.map((suggestion, index) => {

              return (
                <li  key={suggestion} onClick={onClick}>
                  {suggestion}
                </li>
              );
            })}
          </ul>
        );
      } else {
        suggestionsListComponent = (
          <div class="no-suggestions">
            <em>No suggestions!</em>
          </div>
        );
      }
    }

How does it work?

First if conditional checks for showSuggestions and userInput. Than it maps through all the suggestions in the array to create an unordered list.

return (
      <li >
          key={suggestion} 
          onClick={onClick} >
        {suggestion}
      </li>

Selected item gets a different class and all matching items get rendered, the style for selected item is handled using css.

If there are no matches, it returns div with No suggestions.

I just used the variable names inside render method but they aren’t recognized in the render method without this keyword. Destructure this to get these variable in render method.

const {
      onChange,
      onClick,
      onKeyDown,
      state: {
        activeSuggestion,
        filteredSuggestions,
        showSuggestions,
        userInput
      }
    } = this;

This should fix all the errors.

Define onChange function

onChange = (e) => {
    const { suggestions } = this.props;
    const userInput = e.currentTarget.value;

    const filteredSuggestions = suggestions.filter(
      (suggestion) =>
        suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
    );

    this.setState({
      activeSuggestion: 0,
      filteredSuggestions,
      showSuggestions: true,
      userInput: e.currentTarget.value
    });
  };

What does this method do?

  1. Gets suggestions from props
  2. Gets input from an input field
  3. Checks if the input (2) is anywhere in suggestions (1)
  4. The valid entries from 3 are stored in filteredSuggestions state variable.
  5. It also changes input field with the value you type. Otherwise, input field is set by the state and user can’t type.

The current state of React autocomplete component. Where are we?

We’ve created a list and methods to handle change. The application is getting the shape.

I’ve also added some CSS in index.css to make it look better.

Looks better? Yes!

I’ve passed suggestions={[‘White’, ‘Black’, ‘Green’, ‘Blue’, ‘Yellow’, ‘Red’]} to Autocomplete from App.

It shows correct suggestions, but I can not select any option. This react autocomplete application pretty useless, right?

Selecting from options (Keyboard and Mouse)

Selecting an option needs an event listener. Select on mouse click and return key seems reasonable.

Selecting with Mouse Click

There is an onClick event listener on each li of the suggestions.

onClick = (e) => {
    this.setState({
      activeSuggestion: 0,
      filteredSuggestions: [],
      showSuggestions: false,
      userInput: e.currentTarget.innerText
    });
  };

Click on any option, the input field should set to the text from the list item. The application no longer shows suggestions as showSuggestions is set to false.

Adding Key Down Event Listener

To select an option, add a keyDown event listener. Listen for up, down and return keys. Up/Down to switch between options and return to select current option.

13 → Return (Enter) key
38 → Up Arrow Key
40 → Down Arrow Key

onKeyDown = e => {
    const { activeSuggestion, filteredSuggestions } = this.state;

    if (e.keyCode === 13) {
      this.setState({
        activeSuggestion: 0,
        showSuggestions: false,
        userInput: filteredSuggestions[activeSuggestion]
      });
    }
    else if (e.keyCode === 38) {
      if (activeSuggestion === 0) {
        return;
      }

      this.setState({ activeSuggestion: activeSuggestion - 1 });
    }
    else if (e.keyCode === 40) {
      if (activeSuggestion - 1 === filteredSuggestions.length) {
        return;
      }

      this.setState({ activeSuggestion: activeSuggestion + 1 });
    }
  };

Return Key (13):  disables suggestions, resets default suggestion to the first element (0). And sets input field to selected option.

Up Arrow (38): If the first element if suggested, does nothing. Otherwise, selects the upper option.

Down Arrow (40): Just like up arrow, but reverse. Checks if the last element and does nothing. Otherwise, selects lower option.

Here’s the output…

You can select any option from the suggestions.

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Learn More

React - The Complete Guide (incl Hooks, React Router, Redux)
Modern React with Redux [2019 Update]
React Native - The Practical Guide
MERN Stack Front To Back: Full Stack React, Redux & Node.js
Full-Stack Web Apps with Meteor and React
React Hooks
Progressive Web App vs Native app
Getting Started With React.js, Babel 7, Webpack 4, and MobX 5
Push Notifications in PWA (React.js) Using Firebase
Using Typescript with modern React (i.e. hooks, context, suspense)

Originally published at https://programmingwithmosh.com

Suggest