refactor: render cached data directly if available during ssr
This commit is contained in:
parent
bba61c2a37
commit
66076e5841
6 changed files with 84 additions and 29 deletions
|
|
@ -51,6 +51,7 @@ type Props = {
|
|||
repos: Repository[];
|
||||
scheduleNotifs: ScheduleNotification[];
|
||||
mirrors: ScheduleMirror[];
|
||||
snapshots?: Snapshot[];
|
||||
};
|
||||
scheduleId: string;
|
||||
initialSnapshotId?: string;
|
||||
|
|
@ -78,6 +79,7 @@ export function ScheduleDetailsPage(props: Props) {
|
|||
failureReason,
|
||||
} = useQuery({
|
||||
...listSnapshotsOptions({ path: { shortId: schedule.repository.shortId }, query: { backupId: schedule.shortId } }),
|
||||
initialData: loaderData.snapshots,
|
||||
});
|
||||
|
||||
const updateSchedule = useMutation({
|
||||
|
|
|
|||
|
|
@ -5,8 +5,17 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/client/components/ui
|
|||
import { RepositoryInfoTabContent } from "../tabs/info";
|
||||
import { RepositorySnapshotsTabContent } from "../tabs/snapshots";
|
||||
import { useNavigate, useSearch } from "@tanstack/react-router";
|
||||
import type { BackupSchedule, Snapshot } from "~/client/lib/types";
|
||||
|
||||
export default function RepositoryDetailsPage({ repositoryId }: { repositoryId: string }) {
|
||||
export default function RepositoryDetailsPage({
|
||||
repositoryId,
|
||||
initialSnapshots,
|
||||
initialBackupSchedules,
|
||||
}: {
|
||||
repositoryId: string;
|
||||
initialSnapshots?: Snapshot[];
|
||||
initialBackupSchedules?: BackupSchedule[];
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const { tab } = useSearch({ from: "/(dashboard)/repositories/$repositoryId/" });
|
||||
const activeTab = tab || "info";
|
||||
|
|
@ -27,7 +36,11 @@ export default function RepositoryDetailsPage({ repositoryId }: { repositoryId:
|
|||
</TabsContent>
|
||||
<TabsContent value="snapshots">
|
||||
<Suspense>
|
||||
<RepositorySnapshotsTabContent repository={data} />
|
||||
<RepositorySnapshotsTabContent
|
||||
repository={data}
|
||||
initialSnapshots={initialSnapshots}
|
||||
initialBackupSchedules={initialBackupSchedules}
|
||||
/>
|
||||
</Suspense>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
|
|
|||
|
|
@ -11,23 +11,26 @@ import { Button } from "~/client/components/ui/button";
|
|||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/client/components/ui/card";
|
||||
import { Input } from "~/client/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableRow } from "~/client/components/ui/table";
|
||||
import type { Repository, Snapshot } from "~/client/lib/types";
|
||||
import type { BackupSchedule, Repository, Snapshot } from "~/client/lib/types";
|
||||
import { toast } from "sonner";
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
initialSnapshots?: Snapshot[];
|
||||
initialBackupSchedules?: BackupSchedule[];
|
||||
};
|
||||
|
||||
export const RepositorySnapshotsTabContent = ({ repository }: Props) => {
|
||||
export const RepositorySnapshotsTabContent = ({ repository, initialSnapshots, initialBackupSchedules }: Props) => {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const { data, isFetching, failureReason } = useQuery({
|
||||
const { data, isPending, failureReason } = useQuery({
|
||||
...listSnapshotsOptions({ path: { shortId: repository.shortId } }),
|
||||
initialData: [],
|
||||
initialData: initialSnapshots,
|
||||
});
|
||||
|
||||
const schedules = useQuery({
|
||||
...listBackupSchedulesOptions(),
|
||||
initialData: initialBackupSchedules,
|
||||
});
|
||||
|
||||
const refreshMutation = useMutation({
|
||||
|
|
@ -44,7 +47,9 @@ export const RepositorySnapshotsTabContent = ({ repository }: Props) => {
|
|||
refreshMutation.mutate({ path: { shortId: repository.shortId } });
|
||||
};
|
||||
|
||||
const filteredSnapshots = data.filter((snapshot: Snapshot) => {
|
||||
const snapshots = data ?? [];
|
||||
|
||||
const filteredSnapshots = snapshots.filter((snapshot: Snapshot) => {
|
||||
if (!searchQuery) return true;
|
||||
const searchLower = searchQuery.toLowerCase();
|
||||
|
||||
|
|
@ -58,7 +63,7 @@ export const RepositorySnapshotsTabContent = ({ repository }: Props) => {
|
|||
);
|
||||
});
|
||||
|
||||
const hasNoFilteredSnapshots = !filteredSnapshots?.length;
|
||||
const hasNoFilteredSnapshots = !filteredSnapshots.length;
|
||||
|
||||
if (repository.status === "error") {
|
||||
return (
|
||||
|
|
@ -91,7 +96,7 @@ export const RepositorySnapshotsTabContent = ({ repository }: Props) => {
|
|||
);
|
||||
}
|
||||
|
||||
if (isFetching && !data.length) {
|
||||
if (isPending) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="flex items-center justify-center py-12">
|
||||
|
|
@ -101,7 +106,7 @@ export const RepositorySnapshotsTabContent = ({ repository }: Props) => {
|
|||
);
|
||||
}
|
||||
|
||||
if (!data.length) {
|
||||
if (!snapshots.length) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="flex flex-col items-center justify-center text-center py-16 px-4">
|
||||
|
|
@ -131,7 +136,7 @@ export const RepositorySnapshotsTabContent = ({ repository }: Props) => {
|
|||
<div className="flex-1">
|
||||
<CardTitle>Snapshots</CardTitle>
|
||||
<CardDescription className="mt-1">
|
||||
Backup snapshots stored in this repository. Total: {data.length}
|
||||
Backup snapshots stored in this repository. Total: {snapshots.length}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex gap-2 items-center">
|
||||
|
|
@ -180,7 +185,7 @@ export const RepositorySnapshotsTabContent = ({ repository }: Props) => {
|
|||
<span>
|
||||
{hasNoFilteredSnapshots
|
||||
? "No snapshots match filters."
|
||||
: `Showing ${filteredSnapshots.length} of ${data.length}`}
|
||||
: `Showing ${filteredSnapshots.length} of ${snapshots.length}`}
|
||||
</span>
|
||||
</div>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
listSnapshotsOptions,
|
||||
} from "~/client/api-client/@tanstack/react-query.gen";
|
||||
import { ScheduleDetailsPage } from "~/client/modules/backups/routes/backup-details";
|
||||
import { prefetchOrSkip } from "~/utils/prefetch";
|
||||
|
||||
export const Route = createFileRoute("/(dashboard)/backups/$backupId/")({
|
||||
component: RouteComponent,
|
||||
|
|
@ -24,14 +25,21 @@ export const Route = createFileRoute("/(dashboard)/backups/$backupId/")({
|
|||
context.queryClient.ensureQueryData({ ...getScheduleMirrorsOptions({ path: { shortId: backupId } }) }),
|
||||
]);
|
||||
|
||||
void context.queryClient.prefetchQuery({
|
||||
...listSnapshotsOptions({
|
||||
path: { shortId: schedule.repository.shortId },
|
||||
query: { backupId: schedule.shortId },
|
||||
}),
|
||||
const snapshotOptions = listSnapshotsOptions({
|
||||
path: { shortId: schedule.repository.shortId },
|
||||
query: { backupId: schedule.shortId },
|
||||
});
|
||||
|
||||
return { schedule, notifs, repos, scheduleNotifs, mirrors };
|
||||
await prefetchOrSkip(context.queryClient, snapshotOptions);
|
||||
|
||||
return {
|
||||
schedule,
|
||||
notifs,
|
||||
repos,
|
||||
scheduleNotifs,
|
||||
mirrors,
|
||||
snapshots: context.queryClient.getQueryData(snapshotOptions.queryKey),
|
||||
};
|
||||
},
|
||||
staticData: {
|
||||
breadcrumb: (match) => [
|
||||
|
|
|
|||
|
|
@ -6,23 +6,26 @@ import {
|
|||
listSnapshotsOptions,
|
||||
} from "~/client/api-client/@tanstack/react-query.gen";
|
||||
import RepositoryDetailsPage from "~/client/modules/repositories/routes/repository-details";
|
||||
import { prefetchOrSkip } from "~/utils/prefetch";
|
||||
|
||||
export const Route = createFileRoute("/(dashboard)/repositories/$repositoryId/")({
|
||||
component: RouteComponent,
|
||||
errorComponent: (e) => <div>{e.error.message}</div>,
|
||||
loader: async ({ params, context }) => {
|
||||
void context.queryClient.prefetchQuery({
|
||||
...listSnapshotsOptions({ path: { shortId: params.repositoryId } }),
|
||||
});
|
||||
void context.queryClient.prefetchQuery({
|
||||
...listBackupSchedulesOptions(),
|
||||
});
|
||||
const snapshotOptions = listSnapshotsOptions({ path: { shortId: params.repositoryId } });
|
||||
const schedulesOptions = listBackupSchedulesOptions();
|
||||
|
||||
const res = await context.queryClient.ensureQueryData({
|
||||
...getRepositoryOptions({ path: { shortId: params.repositoryId } }),
|
||||
});
|
||||
const [res] = await Promise.all([
|
||||
context.queryClient.ensureQueryData(getRepositoryOptions({ path: { shortId: params.repositoryId } })),
|
||||
prefetchOrSkip(context.queryClient, snapshotOptions),
|
||||
prefetchOrSkip(context.queryClient, schedulesOptions),
|
||||
]);
|
||||
|
||||
return res;
|
||||
return {
|
||||
...res,
|
||||
snapshots: context.queryClient.getQueryData(snapshotOptions.queryKey),
|
||||
backupSchedules: context.queryClient.getQueryData(schedulesOptions.queryKey),
|
||||
};
|
||||
},
|
||||
validateSearch: type({ tab: "string?" }),
|
||||
staticData: {
|
||||
|
|
@ -44,6 +47,13 @@ export const Route = createFileRoute("/(dashboard)/repositories/$repositoryId/")
|
|||
|
||||
function RouteComponent() {
|
||||
const { repositoryId } = Route.useParams();
|
||||
const { snapshots, backupSchedules } = Route.useLoaderData();
|
||||
|
||||
return <RepositoryDetailsPage repositoryId={repositoryId} />;
|
||||
return (
|
||||
<RepositoryDetailsPage
|
||||
repositoryId={repositoryId}
|
||||
initialSnapshots={snapshots}
|
||||
initialBackupSchedules={backupSchedules}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
17
app/utils/prefetch.ts
Normal file
17
app/utils/prefetch.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import type { FetchQueryOptions, QueryClient, QueryKey } from "@tanstack/react-query";
|
||||
|
||||
export async function prefetchOrSkip<
|
||||
TQueryFnData = unknown,
|
||||
TError = Error,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
queryClient: QueryClient,
|
||||
options: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
timeoutMs = 150,
|
||||
): Promise<void> {
|
||||
await Promise.race([
|
||||
queryClient.prefetchQuery(options),
|
||||
new Promise<void>((resolve) => setTimeout(resolve, timeoutMs)),
|
||||
]);
|
||||
}
|
||||
Loading…
Reference in a new issue