#React#Debounce

Debounce with search

Implement a debounce function with a search input. The debounce function should delay the execution of the search function until the user stops typing for 500ms.

By Pratik Rai

Build a search-as-you-type input that fetches recipe results from an API. The key challenges are debouncing user input to avoid excessive API calls, caching results, cancelling outdated requests, and supporting keyboard navigation in the dropdown.

Overview

A single input triggers search against DummyJSON Recipes API. As the user types, results appear in a list below; selecting an item (click or Enter) fills the input. The implementation keeps the UI responsive and avoids redundant network calls.

Architecture

┌─────────────────────────────────────┐
│  DebounceWithSearch                 │
│  - query, results, loading state    │
│  - cache ref, requestId ref         │
└──────────────┬──────────────────────┘
               │
               ├── useDebounce (delay execution)
               ├── fetchResults (cache + request cancellation)
               └── handleKeyDown (ArrowUp/Down, Enter, Escape)

Optimizations:

  1. Debounce — Wait until the user pauses typing (e.g. 300ms) before calling the API.
  2. Cache — Store results by query key and reuse them for repeated searches.
  3. Request cancellation — Use a request ID so only the latest response updates state; older in-flight requests are ignored.
  4. Keyboard — Arrow keys move highlight, Enter selects, Escape closes the list.

useDebounce hook

A small hook that returns a function which delays running a callback until after a quiet period:

TSXcomponent.tsx
1const debounce = useDebounce(); 2// Later: debounce(() => fetchResults(query), 300);

Each new call clears the previous timer, so only the last invocation runs after the delay.

Fetch and request ordering

Before fetching, increment a requestIdRef. After the response arrives, only apply it if the ID still matches the current request. That way, if the user types again and a new request is sent, the older response won’t overwrite newer results.

TSXcomponent.tsx
1const id = ++requestIdRef.current; 2// ... fetch ... 3if (id !== requestIdRef.current) return; 4setResults(data.recipes || []);

Keyboard and selection

  • ArrowDown / ArrowUp — Update a highlighted index (clamped to list length).
  • Enter — If something is highlighted, set the input to that result and clear the list.
  • Escape — Clear results and reset highlight.

The same selection logic runs on click: set query to the item’s name and close the dropdown.

Goal: Implement this demo component within 45-60 minutes.

Frequently asked questions

What makes search-as-you-type harder than adding a debounce?
The debounce is the easy half. The hard half is that requests are slower than the pause you debounce on, so two searches can be in flight at once with no guarantee they return in order. A slow request for "str" can land after a fast one for "strawberry" and overwrite good results with stale ones.
How do you stop an old response overwriting a newer one?
Tag each request and check the tag before applying the result — an incrementing id in a ref, or an `AbortController` per request. Either way the rule is the same: a response for a query the user has moved on from is computed and thrown away, not rendered. Interviewers ask because a demo with one person typing slowly never exposes it.
Where should the debounce live in a React component?
In an effect keyed on the query, with the timer cleared in the cleanup. The cleanup runs before the next effect, so scheduling in the body and clearing in the cleanup means only the final keystroke ever fires a request — no `debounce` utility and no ref holding a timer id.
What are the usual follow-ups?
Caching results by query so revisiting a search costs nothing, keyboard navigation of the results list with arrow keys and Enter, and announcing the result count through an `aria-live` region.

Related Challenges

Continue learning with these related challenges

View All
React

Modal Component

Build an accessible modal dialog component in React with focus trapping, keyboard navigation, backdrop click handling, and portal rendering. A common frontend interview question.

ReactPratik Rai ·

React

Image Carousel

Create an interactive image carousel in React with smooth slide transitions, navigation arrows, dot indicators, autoplay, and touch/swipe support for mobile devices.

React · JavaScriptPratik Rai ·

React

Dynamic Tic Tac Toe

Build a dynamic Tic Tac Toe game in React with customizable grid sizes, win detection algorithms, player turn management, and game reset functionality. Great for interviews.

React · JavaScriptPratik Rai ·