Async AutocompleteLoading saved progress…

Async Autocomplete

Build a search box whose suggestions come from an async source. As the user types, you want to fetch matching results — but firing a request on every keystroke is wasteful and racy. This is the classic autocomplete problem: debounce the typing, show a loading state, and make sure a slow response for an old query never overwrites a newer one.

There's no real network here — a searchAPI(query) helper returns a Promise that resolves a filtered slice of a fixed list after ~400ms, so you can focus on the timing and state logic.

What you'll build

The starter App.tsx renders just the input in its resting state. Make it live:

  1. Debounce. Wait 300ms after the last keystroke before calling searchAPI.
  2. Loading + results. Show Loading… while a request is in flight, then render the results dropdown (or No matches).
  3. Ignore stale responses. If the query changed since a request was fired, throw that response away.
  4. Pick a result. Clicking a result fills the input and closes the dropdown.

Examples

  • Type ap quickly, pause. After 300ms of quiet, one request fires; Loading… shows, then Apple, Apricot, Grape, … appears.
  • Type a, then immediately ap, then app. Only one fetch runs — for app — because each keystroke restarts the timer.
  • A slow response for ap lands after you've typed app. It is discarded, so the list never flashes the wrong matches.

Notes

  • Debounce is a timer you keep resetting. Each keystroke clears the previous setTimeout and starts a new one; the fetch only fires when the timer survives 300ms.
  • Stale responses are a race, not a timing bug. Even with a debounce, two requests can be in flight; tag each with an id and apply only the latest.
  • searchAPI stands in for fetch. In real life you'd also AbortController-cancel the old request; here the id guard is what keeps results correct.
  • Styling is in styles.css; focus on the state and effects.
Loading editor…
Loading preview…