Building an Auto Complete Search Bar Component in React
Here is a step-by-step breakdown and explanation of how you built the Autocomplete Search Bar in React, including key concepts like debouncing, caching, API calls, and conditional rendering.
✅ Objective
You built a live autocomplete search bar that:
- Fetches suggestions from an API as the user types.
- Uses debouncing to avoid too many API calls.
- Caches results to improve performance.
- Displays suggestions dynamically in a dropdown.
✅ 1. Initial Setup and State Management
✅ States used:
const [result, setResult] = useState([]); // Search results from API
const [search, setSearch] = useState(""); // Current search input
const [open, setOpen] = useState(false); // Controls visibility of suggestion box
const [cache, setCahce] = useState({}); // Stores previously fetched results
✅ 2. Fetching Data with Debouncing
✅ fetchData() logic:
const fetchData = async () => {
if (cache[search]) {
setResult(cache[search]); // Use cached result
console.log("Cache return ", search);
return;
}
const data = await fetch(
"https://dummyjson.com/recipes/search?q=" + search
);
const json = await data.json();
console.log("API call ", search);
setResult(json?.recipes);
setCahce((prev) => ({ ...prev, [search]: json?.recipes }));
};- If the result exists in cache, reuse it.
- Else, fetch from the API and store the result in both result and cache.
✅ 3. Debounce API Call using useEffect
useEffect(() => {
const timer = setTimeout(() => fetchData(), 300);
return () => clearTimeout(timer); // Cleanup on re-render or unmount
}, [search]);💡 Why Debounce?
- Prevents unnecessary API calls on every keystroke.
- Waits for 300ms after the user stops typing before calling the API.
✅ 4. Input Box UI and Interaction
<input
type="text"
className="input_box"
value={search}
onChange={(e) => setSearch(e.target.value)}
onFocus={() => setOpen(true)}
onBlur={() => setOpen(false)}
/>✅ Features:
- Controlled input with search state.
- Opens dropdown on focus.
- Hides dropdown on blur.
✅ 5. Conditional Rendering of Suggestions
{search && open && (
<ul className="result-box">
{result.map((val) => (
<li className="result" key={val.id}>
{val.name}
</li>
))}
</ul>
)}✅ Logic:
- Render suggestions only if:
- search is non-empty
- Input is focused (open === true)
- Maps result array and displays each name as a list item.
✅ 6. Complete Code Recap
export default function App() {
const [result, setResult] = useState([]);
const [search, setSearch] = useState("");
const [open, setOpen] = useState(false);
const [cache, setCahce] = useState({});
const fetchData = async () => {
if (cache[search]) {
setResult(cache[search]);
return;
}
const data = await fetch("https://dummyjson.com/recipes/search?q=" + search);
const json = await data.json();
setResult(json?.recipes);
setCahce((prev) => ({ ...prev, [search]: json?.recipes }));
};
useEffect(() => {
const timer = setTimeout(() => fetchData(), 300);
return () => clearTimeout(timer);
}, [search]);
return (
<div className="App">
<h1>Machine Coding Round</h1>
<h2>Autocomplete Search Bar</h2>
<input
type="text"
className="input_box"
value={search}
onChange={(e) => setSearch(e.target.value)}
onFocus={() => setOpen(true)}
onBlur={() => setOpen(false)}
/>
{search && open && (
<ul className="result-box">
{result.map((val) => (
<li className="result" key={val.id}>
{val.name}
</li>
))}
</ul>
)}
</div>
);
}
✅ Extra Ideas to Enhance It
- Highlight matching keywords in suggestions
- Add keyboard navigation (arrow keys to select).
- Add loading spinner while fetching data.
- Show No results found if result.length === 0.
- Provides a smooth user experience like most OTP inputs on modern apps