Add the paged product catalog grid
React · React · advanced · modification
Adds the paged catalog grid. `usePagination` owns the page state and the data fetching: it tracks the current page for the products API, guards `next`/`prev` at the boundaries, and ignores stale responses so quick clicking can never render the wrong page's products. `CatalogGrid` renders the grid with loading, error, and empty states and wires the pager buttons to the hook. `src/api/products.js` is shown unchanged for reviewer context. Tested locally: the grid loads and Next/Previous page through the catalog, with the buttons disabling at the ends.
Storefront catalog page. The catalog holds a few hundred products — about 12 pages at the default page size of 24.
Requirements
- Add the paged product catalog: fetch one page of products at a time from the products API and render them as a grid with Previous / Next controls.
- The products API contract is fixed and documented in src/api/products.js: "the `page` query param is 1-based — page 1 is the first page. The server clamps any `page` < 1 up to 1 instead of erroring." The response is `{ items, totalPages }`, where `totalPages` is 0 when the catalog is empty. The client must request pages in the API's coordinates.
- Paging with Next must visit every page exactly once: no page of products is ever shown twice, and the last page (page = totalPages) must be reachable.
- Next is disabled on the last page and Previous on the first page.
- The grid handles loading, error, and empty-catalog states.
Files touched
- src/api/products.js
- src/hooks/usePagination.js
- src/components/CatalogGrid.jsx
--- src/api/products.js
/**
* Product catalog API client.
*
* Contract (services/catalog, v2): the `page` query param is 1-based —
* page 1 is the first page. The server clamps any `page` < 1 up to 1
* instead of erroring. Responds with `{ items, totalPages }`, where
* `totalPages` is the page count at the requested pageSize (0 when the
* catalog is empty).
*/
export async function fetchProducts({ page, pageSize = 24 }) {
const res = await fetch(`/api/products?page=${page}&pageSize=${pageSize}`);
if (!res.ok) {
throw new Error(`Products request failed: ${res.status}`);
}
return res.json();
}
--- src/hooks/usePagination.js
+import { useEffect, useState } from "react";
+
+import { fetchProducts } from "../api/products";
+
+/**
+ * Pages through the product catalog. `page` is the current page for the
+ * products API; `items` holds the products on that page.
+ */
+export function usePagination(pageSize) {
+ const [page, setPage] = useState(0);
+ const [totalPages, setTotalPages] = useState(0);