How to Think About This Problem
This is an API lifecycle problem disguised as a product grid. The difficult part is not rendering cards. The difficult part is making search, filters, pagination, retry, and cancellation work together.
A senior implementation treats every request as a lifecycle: start, maybe retry, succeed, fail, or get cancelled because the user changed input.
State Model
Keep UI state and request state separate:
query
category
page
products
totalPages
loading
message
The query, category, and page describe what the user wants. Products and totalPages describe the latest successful response. Loading and message describe the current request state.
Cancellation Protects Against Stale Results
Users type quickly. A request for "hea" can finish after a request for "headphones". If you let the older response update state, the UI shows the wrong results.
controllerRef.current?.abort();
const controller = new AbortController();
controllerRef.current = controller;
Aborting the previous request makes the old work irrelevant. In production you might also use a request id guard, especially if the data source cannot be aborted.
Retry Logic
Retry should be deliberate. Retry temporary failures, not bad user input. Keep the retry loop inside one request function so loading and error states remain consistent.
for (let attempt = 1; attempt <= 3; attempt += 1) {
try {
return await fetchProducts(...);
} catch (error) {
if (error.name === 'AbortError') return;
if (attempt === 3) throw error;
await wait(700, signal);
}
}
Pagination Reset
When search or category changes, reset to page one. Otherwise the user may be on page three of a previous result set, change the filter, and see an empty page even though matching products exist on page one.
Step-by-Step Build
- Render products from static mock data first.
- Add query and category filters.
- Add page state and previous/next controls.
- Wrap data loading in an async function.
- Add AbortController to cancel stale requests.
- Add retry messaging and final failure state.
Common Pitfall: Retrying aborted requestsIf the user changes the search input, the old request is intentionally cancelled. Do not retry it. Treat AbortError as a normal exit path.
Edge Cases
- No products match the search.
- Query changes while a request is retrying.
- Retry fails three times and shows a final error.
- Pagination buttons are disabled during loading.
- Filter changes reset page to one.
Interview TipSay: "I separate user intent from request lifecycle state. When intent changes, I cancel stale work, reset pagination if needed, and let only the latest request update the grid."
Remember: For Product Listing with API Retry: nail the state model before JSX — Request lifecycle, Retry behavior, Stale protection.
Say this in the interview: I would clarify interactions for Product Listing with API Retry, model state and derived UI first, then build components around that invariant.