Forem

Cover image for Building an autocomplete-input component in Next.js
Prodipta Banerjee
Prodipta Banerjee

Posted on

1

Building an autocomplete-input component in Next.js

Sandbox Demo

Introduction:
AutoComplete input fields are a common user interface element used to help users efficiently find and select options from a predefined list. In this blog, we'll explore how to create a customizable AutoComplete input component in React with throttling to improve performance. We'll walk through the code for the component and explain its key features.

Creating the AutoCompleteInput Component:
The AutoCompleteInput component is designed to provide a user-friendly AutoComplete experience. Let's break down its important components and functionality:

  1. Props:
    The component takes two props: options and handleSelection. The options prop is an array of strings representing the available options, while handleSelection is a callback function to handle the selected option.

  2. State and Refs:

    • inputRef: This useRef hook is used to reference the input element for handling clicks outside the component.
    • value: Represents the current value of the input field.
    • suggestions: Stores the list of suggestions based on the user's input.
  3. getSuggestions Function:
    This function filters the available options based on the user's input. It converts both the input and options to lowercase for case-insensitive matching.

  4. onChange Function:
    Whenever the input field's value changes, this function updates the value state and recalculates the suggestions based on the new value.

  5. onSuggestionClick Function:
    When a suggestion is clicked, this function sets the selected suggestion as the input value and calls the handleSelection callback with the selected value.

  6. isSuggestionEmpty Function:
    This function checks if the suggestions list is empty or contains only an empty string. It is used to conditionally render the suggestions dropdown.

  7. Event Listener for Clicks Outside:
    An useEffect hook is used to add a click event listener to the document body. This listener detects clicks outside of the component, allowing it to blur and hide the suggestions dropdown.

  8. Throttling with Lodash:
    To improve performance and responsiveness, we've added throttling to the onChange event using the lodash/debounce function. This reduces the frequency of function calls while the user is typing rapidly.

Installing Lodash:
Before implementing throttling, make sure to install the lodash library using npm. Run the following command in your project directory:

npm install lodash
Enter fullscreen mode Exit fullscreen mode

Rendering the Component:
The AutoCompleteInput component is rendered within the Home component. When the user selects a suggestion, the selected value is logged to the console.

Conclusion:
Creating an AutoComplete input component in React can enhance user experience when searching or selecting items from a list. The provided code demonstrates a basic implementation, and with the added throttling, it ensures smooth performance even with rapid typing.

Feel free to use this code as a starting point and adapt it to your specific needs. By following the installation instructions for lodash, you can easily add throttling to your React components. Happy coding!


"use client";
import AutoCompleteInput from "./components/AutoComplete";

const data = ["One", "Two", "Three"];

export default function Home() {
  const handleSelection = (selectedOption: string) => {
    console.log({ Selected: { selectedOption } });
  };

  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <AutoCompleteInput
        options={data.map((v: string) => v)}
        handleSelection={handleSelection}
      />
    </main>
  );
}

Enter fullscreen mode Exit fullscreen mode
import React, { useEffect, useRef, useState } from "react";
import debounce from "lodash/debounce"; // npm install lodash

interface IPropType {
  options: string[];
  handleSelection: (val: string) => void;
}

const AutoCompleteInput = (props: IPropType) => {
  const { options, handleSelection } = props;
  const inputRef = useRef<HTMLInputElement>(null);
  const [value, setValue] = useState("");
  const [suggestions, setSuggestions] = useState([""]);

  const getSuggestions = (inputValue: string) => {
    if (typeof inputValue !== "string") {
      return [];
    }
    const inputValueLowerCase = inputValue.toLowerCase();
    return options.filter((option) =>
      option.toLowerCase().includes(inputValueLowerCase),
    );
  };

  // Debounce the onChange function
  const debouncedOnChange = debounce((newValue: string) => {
    setValue(newValue);
    setSuggestions(getSuggestions(newValue));
  }, 100); // Adjust the debounce delay as needed (e.g., 300 milliseconds)

  const onSuggestionClick = (suggestion: string) => {
    setValue(suggestion);
    handleSelection(suggestion);
    setSuggestions([]);
  };

  const isSuggestionEmpty = () => {
    if (suggestions.length === 1 && suggestions[0] === "") {
      return true;
    } else return false;
  };

  // Add a click event listener to the document body to handle clicks outside of the component
  useEffect(() => {
    const handleDocumentClick = (e: any) => {
      if (inputRef.current && !inputRef.current.contains(e.target)) {
        inputRef.current.blur();
        setSuggestions([]);
      }
    };

    document.addEventListener("click", handleDocumentClick);

    return () => {
      document.removeEventListener("click", handleDocumentClick);
    };
  }, []);

  return (
    <div className="relative">
      <input
        ref={inputRef}
        className="w-full border border-dark text-black transition-all duration-300 rounded-md px-4 py-3 focus:outline-none"
        type="text"
        placeholder="Search"
        value={value}
        onChange={(e) => debouncedOnChange(e.target.value)}
        onFocus={() => {
          setSuggestions(options);
          setValue("");
        }}
      />
      {!isSuggestionEmpty() && suggestions.length > 0 && (
        <ul
          className="bg-white border-blue-500 border-2 rounded hover:cursor-pointer absolute top-14 w-full z-20 max-h-64 overflow-y-auto"
          onPointerLeave={() => {
            inputRef?.current?.blur();
            setSuggestions([""]);
          }}
        >
          {suggestions.map((suggestion) => (
            <li
              key={suggestion}
              className="hover:bg-blue-500 hover:text-white transition duration-200 text-sm text-gray-700 p-1"
              onClick={() => onSuggestionClick(suggestion)}
            >
              {suggestion}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default AutoCompleteInput;


Enter fullscreen mode Exit fullscreen mode

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay