React Machine Coding: Debounced Search, Infinite Scroll &
Custom useFetch Hook
Detailed explanation + practical examples for frontend interviews
Overview: Ye PDF 3 common React machine-coding tasks ko simple language me explain karta hai. Har
topic me concept, approach, edge cases, interview points aur working React example diya gaya hai.
1. Debounced Search Input
Problem: Search input me API call tabhi karni hai jab user typing stop kare. Har key press par API call karna
performance aur backend dono ke liye bad hota hai.
Debounce kya hota hai? Debounce ek technique hai jisme function ko delay ke baad run kiya jata hai. Agar
delay ke andar user dobara type karta hai, purana timer cancel ho jata hai aur naya timer start hota hai.
Use case: search bar, autocomplete, city/property search, product search.
Typical delay: 300ms to 600ms.
Best practice: empty query par API call avoid karo, loading aur error state handle karo.
import { useEffect, useState } from "react";
export default function DebouncedSearch() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (![Link]()) {
setResults([]);
return;
}
const timerId = setTimeout(async () => {
try {
setLoading(true);
const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
const data = await [Link]();
setResults([Link] || []);
} catch (err) {
[Link]("Search failed", err);
} finally {
setLoading(false);
}
}, 500);
return () => clearTimeout(timerId);
}, [query]);
return (
<div className="max-w-xl mx-auto p-4">
<input
className="w-full rounded-xl border p-3"
placeholder="Search..."
value={query}
onChange={(e) => setQuery([Link])}
/>
{loading && <p>Searching...</p>}
<ul>
{[Link]((item) => (
<li key={[Link]}>{[Link]}</li>
))}
</ul>
</div>
);
}
Interview Tip: Interviewer ko batao ki cleanup function clearTimeout karta hai, isliye previous timer cancel
hota hai. Isse unnecessary API calls stop hoti hain.
Debounce Custom Hook Example
import { useEffect, useState } from "react";
function useDebounce(value, delay = 500) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
export default function SearchWithHook() {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 500);
useEffect(() => {
if (!debouncedQuery) return;
// call API here using debouncedQuery
}, [debouncedQuery]);
return <input value={query} onChange={(e) => setQuery([Link])} />;
}
2. Infinite Scroll
Problem: Jab user page ke bottom ke paas pahunch jaye, tab next page ka data automatically load karna
hai. Isme Intersection Observer API sabse clean approach hai.
Intersection Observer kya karta hai? Ye browser API observe karti hai ki koi element viewport me visible
hua ya nahi. Hum list ke end me ek sentinel div lagate hain. Jaise hi wo visible hota hai, next page fetch karte
hain.
Avoid scroll event spam: Intersection Observer efficient hota hai.
State required: items, page, loading, hasMore.
Duplicate calls avoid karne ke liye loading check zaroor rakho.
import { useCallback, useEffect, useRef, useState } from "react";
export default function InfiniteScrollList() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const observerRef = useRef(null);
const loadItems = async (pageNo) => {
if (loading || !hasMore) return;
try {
setLoading(true);
const res = await fetch(`/api/products?page=${pageNo}&limit=10`);
const data = await [Link]();
setItems((prev) => [...prev, ...([Link] || [])]);
setHasMore([Link]);
} catch (err) {
[Link]("Failed to load items", err);
} finally {
setLoading(false);
}
};
useEffect(() => {
loadItems(page);
}, [page]);
const lastItemRef = useCallback(
(node) => {
if (loading) return;
if ([Link]) [Link]();
[Link] = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasMore) {
setPage((prev) => prev + 1);
}
});
if (node) [Link](node);
},
[loading, hasMore]
);
return (
<div className="max-w-2xl mx-auto p-4 space-y-3">
{[Link]((item, index) => {
const isLast = index === [Link] - 1;
return (
<div
ref={isLast ? lastItemRef : null}
key={[Link]}
className="rounded-xl border p-4"
>
{[Link]}
</div>
);
})}
{loading && <p>Loading more...</p>}
{!hasMore && <p>No more data.</p>}
</div>
);
}
Interview Tip: Infinite scroll me observer disconnect karna important hai, warna multiple observers create
ho sakte hain aur duplicate API calls aa sakti hain.
3. Custom useFetch Hook
Problem: Har component me loading, error, data handling repeat hota hai. Isliye reusable custom hook
banana chahiye.
Custom hook ka benefit: Code reusable, clean aur testable ho jata hai. Components sirf UI handle karte
hain, data fetching hook ke andar chali jati hai.
Return values: data, loading, error, refetch.
AbortController use karo taaki component unmount hone par request cancel ho jaye.
Dependency change hone par API dobara call ho sakti hai.
import { useCallback, useEffect, useState } from "react";
export function useFetch(url, options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchData = useCallback(async () => {
const controller = new AbortController();
try {
setLoading(true);
setError(null);
const res = await fetch(url, {
...options,
signal: [Link],
});
if (![Link]) {
throw new Error(`HTTP Error: ${[Link]}`);
}
const json = await [Link]();
setData(json);
} catch (err) {
if ([Link] !== "AbortError") {
setError([Link] || "Something went wrong");
}
} finally {
setLoading(false);
}
return () => [Link]();
}, [url]);
useEffect(() => {
const cleanupPromise = fetchData();
return () => {
[Link]((cleanup) => cleanup && cleanup());
};
}, [fetchData]);
return { data, loading, error, refetch: fetchData };
}
Using the useFetch Hook
import { useFetch } from "./useFetch";
export default function UsersList() {
const { data, loading, error, refetch } = useFetch("/api/users");
if (loading) return <p>Loading users...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
<button onClick={refetch}>Refresh</button>
<ul>
{data?.users?.map((user) => (
<li key={[Link]}>{[Link]}</li>
))}
</ul>
</div>
);
}
Interview Tip: Better version me AbortController directly useEffect ke andar create karna simple hota hai.
Interview me race condition aur unmount cleanup mention karna plus point hai.
Quick Interview Comparison
Topic Main Idea Best API/Hook Common Mistake
Debounced Search Typing stop hone ke setTimeout + Every keystroke par API
baad API call clearTimeout / call
useDebounce
Infinite Scroll Bottom visible hote hi Intersection Observer Duplicate calls and no
next data load hasMore check
Custom useFetch Reusable useEffect + useState + Unmount cleanup miss
loading/error/data logic AbortController karna