DEV Community

Ramil
Ramil

Posted on • Edited on

How to create autocomplete with react-autocomplete-pure

Autocomplete in input fields is a very useful feature that allows customers to improve their UX when using your site.
One of the features of autocomplete is that we help the user to enter some data, not forcing him to enter the entire text, but by providing a ready-made set of options. Thanks to this approach, the user can choose exactly from those options that the application knows how to work with, saving us from unnecessary checks and errors.

One of the first ready-made solutions that come to mind are libraries such as react-autocomplete and react-autosuggest. These are great libraries that do exactly what they are supposed to do: when you enter a value in the input field, they display a dropdown with possible options for substitution. Unfortunately, these libraries are no longer actively supported by their maintainers (react-autosuggest is looking for the main maintainer, and react-autocomplete is in the archive).

In this regard, I decided to write (yes, this is sooo classic ๐Ÿ˜„) my vision of a component for autocompletion.

Let me introduce react-autocomplete-pure - TypeScript friendly react component for autocomplete.
The main features that I wanted to put into this component is that I developed it with the ability to have the finest possible configuration of everything that may be required when developing specifically for your project.

Below is the main key features which react-autocomplete-pure gives to you:

  • the maximum setting for displaying all parts of the component (input field, list, managing the view of the list container and its composition);
  • written in TypeScript, which makes it possible to take full advantage of typings with support for generics;
  • keyboard events support;
  • a11y support;

react-autocomplete-pure has almost no state of its own, which means it needs to be managed in the parent component. This keeps the component as dummy as possible, which allows us to keep all the logic in one place and will only manage the display based on passed props.

Example of using

Suppose the user wants to enter in our input field the name of a movie, from the imdb's top 100 movies. Well, there is nothing easier! Let's add a field for autocompletion and show the user the available movie options as they type.

First, let's install react-autocomplete-pure to our project
using npm

npm i react-autocomplete-pure
Enter fullscreen mode Exit fullscreen mode

or via yarn

yarn add react-autocomplete-pure
Enter fullscreen mode Exit fullscreen mode

NOTE: Before continuing, I would like to clarify that the code below is written in TypeScript so that we have type support and better DX when working with our IDE. Optionally, types can be omitted if you want to write in Vanilla JS.

We know that movies will come to us as an array of objects from our backend (example). Each object in this array is a movie with its title and release year.

type Film = { title: string; year: number };
const topFilms: Film[] = [
  { title: "The Shawshank Redemption", year: 1994 },
  { title: "The Godfather", year: 1972 },
  /*...and more...*/
];
Enter fullscreen mode Exit fullscreen mode

So, as we know incoming data format, now it's time to add the component to the project:

import { AutocompletePure } from "react-autocomplete-pure";
import { Film } from './types';

export function App() {
  return (
    <div>
      <h1>My awesome app with autocomplete</h1>
      <AutocompletePure<Film>  /*what about required props?*/>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

We've added a component to the project, but we haven't added any props to it yet. Let's fix this.
According to the available props in documentation we have some required props.
Don't be afraid that there are so many of them, they are all intuitive and it is thanks to them that you can fully control the behavior of the component ๐Ÿ˜„. Let's update our code.

NOTE: fetchFilms function is taken from here.

import { useCallback, useEffect, useRef, useState } from "react";
import { AutocompletePure, RenderItem } from "react-autocomplete-pure";
import { fetchFilms } from "./mock";
import { Film } from "./types";

// let's add some style if item is highlighted
const renderItem: RenderItem<Film> = (item, { isHighlighted }) => (
  <span style={{ fontWeight: isHighlighted ? 700 : 400 }}>{item.title}</span>
);

// Needs to get new value when using keyboard events
const getSuggestionValue = (item: Film) => item.title;

export function App() {
  const [isOpen, setIsOpen] = useState<boolean>(false);
  const [suggestions, setSuggestions] = useState<Film[]>([]);
  const [value, setValue] = useState<string>("");

  // When input changes then save value
  // If change reason is type on input then get new items, save them and close dropdown if no new items fetched
  // If change reason is enter keydown then simple close dropdown
  const handleChange: AutocompletePureProps<Film>["onChange"] = useCallback(
    async (_event, { value, reason }) => {
      setValue(value);
      if (reason === "INPUT") {
        const newFilms = await fetchFilms(value);
        setSuggestions(newFilms);
        setIsOpen(Boolean(newFilms.length));
      } else if (reason === "ENTER") {
        setIsOpen(false);
      }
    },
    []
  );

  // When item selected then save it and close dropdown
  const handleSelect: AutocompletePureProps<Film>["onSelect"] = useCallback(
    (_event, { item }) => {
      const value = getSuggestionValue(item);
      setValue(value);
      setIsOpen(false);
    },
    []
  );

  return (
    <div>
      <h1>My awesome app with autocomplete</h1>
      <AutocompletePure<Film>
        open={isOpen}
        value={value}
        items={suggestions}
        onChange={handleChange}
        onSelect={handleSelect}
      />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Our component is almost ready to use, except that we currently do not hide the list if we click somewhere outside the component. This is easy to fix, the component can calls the onClickOutside callback, in which we can implement the logic for hiding the list.

/* ...same as before... */
export function App() {
  /* ...same as before... */
  const handleClickOutside = useCallback((_event: Event) => {
    setIsOpen(false);
  }, []);

  /* ...same as before... */
  return (
    <AutocompletePure<Film>
      open={isOpen}
      value={value}
      items={suggestions}
      onChange={handleChange}
      onSelect={handleSelect}
      onClickOutside={handleClickOutside}
    />
  );
  /* ...same as before... */
}
Enter fullscreen mode Exit fullscreen mode

That's all, now the component can be fully used! Congratulations, you did it! You can play more in sandbox:

If you want to see more features (like custom renderers for component's parts) of using component you can watch them in repository in site folder

Top comments (0)