DEV Community

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

Posted on

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

Top comments (0)