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:
@@ -1,16 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
type Label = { id: number; name: string; labeltypeId: string | null };
|
||||
type Paged<T> = { items: T[]; page: number; pageSize: number; total: number };
|
||||
|
||||
export default function LabelsSearch() {
|
||||
export default function LabelsSearch({ initial }: { initial?: Paged<Label> }) {
|
||||
const [q, setQ] = useState("");
|
||||
const [page, setPage] = useState(1);
|
||||
const [data, setData] = useState<Paged<Label> | null>(null);
|
||||
const [data, setData] = useState<Paged<Label> | null>(initial ?? null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const firstLoadSkipped = useRef(false);
|
||||
const pageSize = 20;
|
||||
const totalPages = useMemo(() => (data ? Math.max(1, Math.ceil(data.total / data.pageSize)) : 1), [data]);
|
||||
|
||||
@@ -32,6 +33,11 @@ export default function LabelsSearch() {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// If server provided initial data for first page of empty search, skip first fetch
|
||||
if (!firstLoadSkipped.current && initial && !q && page === 1) {
|
||||
firstLoadSkipped.current = true;
|
||||
return;
|
||||
}
|
||||
load();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [page]);
|
||||
|
||||
Reference in New Issue
Block a user