DEV Community

Cover image for Building a Debounced Input Component in React with TypeScript
Anton Martyniuk
Anton Martyniuk

Posted on • Originally published at antondevtips.com on

Building a Debounced Input Component in React with TypeScript

Introduction

Let’s explore a web application that fetches data from an API based on the user’s input. Everytime user enters a character in a search input — a request is sent to get filtered data from the API. If we implement an application this way we’ll experience a common performance issue related to rapid state updates.

This is where debouncing comes into play. Debouncing is a programming practice used to ensure that time-consuming operations are not executed too often. A debounced input delays the processing of the input’s keyup event until the user has stopped typing for a given amount of time (often milliseconds or seconds). If the user starts typing again before this time is up, the timer resets. This approach prevents the application from making excessive calls to APIs or performing redundant operations, thus enhancing performance, UI responsiveness and overall user experience.

Implementing Debounced Input in React with TypeScript

Let’s create a simple debounced input component in React using TypeScript. We’ll use use-debounce NPM package for the debounce function:

import React, { useState, useEffect } from "react";
import { useDebounce } from "use-debounce";

type DebouncedInputProps = {
    value: string;
    onChange: (value: string) => void;
    placeholder?: string;
    delay?: number; // Delay in milliseconds
}

export const DebouncedInput: React.FC<DebouncedInputProps>
    = ({ value, onChange, placeholder = '', delay = 250, }) => {
    const [inputValue, setInputValue] = useState(value);

    // Create a debounced value
    const [debouncedValue] = useDebounce(inputValue, delay);

    /**
     * Call the onChange function when a user stops typing
     */
    useEffect(() => {
        onChange(debouncedValue);
    }, [debouncedValue]);

    return (
        <input
            type="text"
            value={inputValue}
            onChange={(e) => setInputValue(e.target.value)}
            placeholder={placeholder}
        />
    );
};
Enter fullscreen mode Exit fullscreen mode

Here an input value in the component’s state is updated everytime a user enters a character. But DebouncedInput component calls the onChange function only when the user stops typing for more than a given delay.

onChange function reports a debounced value changes to the parent component. Let’s create a page that utilizes the DebouncedInput component to fetch products bases on a user input. First, we need to define some products that will be fetched:

type Product = {
  id: number,
  title: string
}

const products: Product[] = [
    { id: 1, title: "Samsung Galaxy S24" },
    { id: 2, title: "Samsung Galaxy Fold 4" },
    { id: 3, title: "iPhone 14 pro" },
    { id: 4, title: "One Plus 7" },
    { id: 5, title: "Xiaomi 14 Pro" }
];
Enter fullscreen mode Exit fullscreen mode

Now let’s create a SearchPage component.

export const SearchPage: React.FC = () => {
    const [filteredProducts, setFilteredProducts] = useState<Product[]>(products);

    return (
        <div>
            <h1>Debounced Search Example</h1>
            <DebouncedInput
                value={''}
                onChange={searchProductsAsync}
                placeholder="Type to search..."
                delay={250} // Debounce delay in milliseconds
            />
            <div>
                {filteredProducts.length > 0 ? (
                    <div>
                        {filteredProducts.map(product => (
                            <div key={product.id}>{product.title}</div>
                        ))}
                    </div>
                ) : (
                    <p>No products found.</p>
                )}
            </div>
        </div>
    );
};
Enter fullscreen mode Exit fullscreen mode

Here we render a DebouncedInput where the user enters a name of the product to search for. Then we need to call an API to get filtered products.

Here is a function callback for a DebouncedInput that fetches products when a user stops typing for 250ms:

const searchProductsAsync = async (searchText: string) => {
    // Simulating a network request with a delay
    await new Promise(resolve => setTimeout(resolve, 250));

    if (!searchText.trim()) {
        setFilteredProducts(products);
        return;
    }

    const lowercasedFilter = searchText.toLowerCase();

    const filteredData = products.filter(p =>
        p.title.toLowerCase().includes(lowercasedFilter)
    );

    setFilteredProducts(filteredData);
};
Enter fullscreen mode Exit fullscreen mode

This implementation with debounced input prevents our API from being called too often, making the web page more performant and UI more responsive.

Summary

By implementing a debounced input in React, you can significantly enhance the user experience in your applications, especially in scenarios where inputs trigger time-consuming operations like API calls. This example demonstrates the power of debouncing in managing efficient state updates and provides a reusable DebouncedInput component that can be easily integrated into any React project. A debounced input is easy to implement and it not only improves performance but also make user interface more responsive, making it an essential technique in the modern web developer’s toolkit.

Hope you find this blog post useful. Happy coding!

Originally published at https://antondevtips.com.

After reading the post consider the following:

  • Subscribe to receive newsletters with the latest blog posts
  • Download the source code for this post from my github (available for my sponsors on BuyMeACoffee and Patreon)

If you like my content —  consider supporting me

Unlock exclusive access to the source code from the blog posts by joining my Patreon and Buy Me A Coffee communities!

Top comments (0)