perf(zxdb): server-render index pages with ISR and initial data
Why - Reduce time-to-first-content on ZXDB index pages by eliminating the initial client-side fetch and enabling incremental static regeneration. What - Main Explorer (/zxdb): - Server-renders first page of results and lookup lists (genres, languages, machinetypes) and passes them as initial props. - Keeps client interactivity for subsequent searches/filters. - Labels index (/zxdb/labels): - Server-renders first page of empty search and passes as initial props to skip the first fetch. - Category lists: - Genres (/zxdb/genres), Languages (/zxdb/languages), Machine Types (/zxdb/machinetypes) now server-render their lists and export revalidate=3600. - Refactored list components to accept server-provided items; removed on-mount fetching. - Links & prefetch: - Replaced remaining anchors with Next Link to enable prefetch where applicable. Tech details - Added revalidate=3600 to the index pages for ISR. - Updated ZxdbExplorer to accept initial results and initial filter lists; skips first client fetch when initial props are present. - Updated LabelsSearch to accept initial payload and skip first fetch in default state. - Updated GenreList, LanguageList, MachineTypeList to be presentational components receiving items from server pages. Notes - Low-churn list APIs already emit Cache-Control for CDN; list pages now render instantly from server. - Further polish (breadcrumbs, facet counts UI) can build on this foundation without reintroducing initial network waits. Signed-off-by: Junie@lucy.xalior.com
This commit is contained in:
@@ -18,14 +18,24 @@ type Paged<T> = {
|
||||
total: number;
|
||||
};
|
||||
|
||||
export default function ZxdbExplorer() {
|
||||
export default function ZxdbExplorer({
|
||||
initial,
|
||||
initialGenres,
|
||||
initialLanguages,
|
||||
initialMachines,
|
||||
}: {
|
||||
initial?: Paged<Item>;
|
||||
initialGenres?: { id: number; name: string }[];
|
||||
initialLanguages?: { id: string; name: string }[];
|
||||
initialMachines?: { id: number; name: string }[];
|
||||
}) {
|
||||
const [q, setQ] = useState("");
|
||||
const [page, setPage] = useState(1);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<Paged<Item> | null>(null);
|
||||
const [genres, setGenres] = useState<{ id: number; name: string }[]>([]);
|
||||
const [languages, setLanguages] = useState<{ id: string; name: string }[]>([]);
|
||||
const [machines, setMachines] = useState<{ id: number; name: string }[]>([]);
|
||||
const [data, setData] = useState<Paged<Item> | null>(initial ?? null);
|
||||
const [genres, setGenres] = useState<{ id: number; name: string }[]>(initialGenres ?? []);
|
||||
const [languages, setLanguages] = useState<{ id: string; name: string }[]>(initialLanguages ?? []);
|
||||
const [machines, setMachines] = useState<{ id: number; name: string }[]>(initialMachines ?? []);
|
||||
const [genreId, setGenreId] = useState<number | "">("");
|
||||
const [languageId, setLanguageId] = useState<string | "">("");
|
||||
const [machinetypeId, setMachinetypeId] = useState<number | "">("");
|
||||
@@ -59,18 +69,23 @@ export default function ZxdbExplorer() {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Avoid immediate client fetch on first paint if server provided initial data
|
||||
if (initial && page === 1 && q === "" && genreId === "" && languageId === "" && machinetypeId === "" && sort === "title") {
|
||||
return;
|
||||
}
|
||||
fetchData(q, page);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [page, genreId, languageId, machinetypeId, sort]);
|
||||
|
||||
// Load filter lists once
|
||||
// Load filter lists on mount only if not provided by server
|
||||
useEffect(() => {
|
||||
if (initialGenres && initialLanguages && initialMachines) return;
|
||||
async function loadLists() {
|
||||
try {
|
||||
const [g, l, m] = await Promise.all([
|
||||
fetch("/api/zxdb/genres", { cache: "no-store" }).then((r) => r.json()),
|
||||
fetch("/api/zxdb/languages", { cache: "no-store" }).then((r) => r.json()),
|
||||
fetch("/api/zxdb/machinetypes", { cache: "no-store" }).then((r) => r.json()),
|
||||
fetch("/api/zxdb/genres", { cache: "force-cache" }).then((r) => r.json()),
|
||||
fetch("/api/zxdb/languages", { cache: "force-cache" }).then((r) => r.json()),
|
||||
fetch("/api/zxdb/machinetypes", { cache: "force-cache" }).then((r) => r.json()),
|
||||
]);
|
||||
setGenres(g.items ?? []);
|
||||
setLanguages(l.items ?? []);
|
||||
@@ -78,7 +93,7 @@ export default function ZxdbExplorer() {
|
||||
} catch {}
|
||||
}
|
||||
loadLists();
|
||||
}, []);
|
||||
}, [initialGenres, initialLanguages, initialMachines]);
|
||||
|
||||
function onSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user