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