Loading UI ба Suspense
Server Component-д өгөгдөл татахад тодорхой хугацаа шаарддаг. Тэр хугацаанд хэрэглэгч хоосон дэлгэц харвал муу туршлага болно. Next.js-д loading.tsx файл болон React-н Suspense ашиглан уншиж байгаа үеийн UI-г хялбархан харуулж болно.
loading.tsx — Автомат loading UI
page.tsx-тай нэг директорт loading.tsx нэмэхэд Next.js тухайн хуудас ачаалагдаж байх үед автоматаар харуулдаг:
app/
└── courses/
├── page.tsx ← Өгөгдөл татдаг хуудас
└── loading.tsx ← Татаж байх үеийн UI
// app/courses/loading.tsx
export default function CoursesLoading() {
return (
<div className="max-w-6xl mx-auto px-4 py-8">
<div className="h-8 w-48 bg-[#1e293b] rounded animate-pulse mb-6" />
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{Array.from({ length: 6 }).map((_, i) => (
<div
key={i}
className="h-40 bg-[#0f172a] border border-[#1e293b] rounded-xl animate-pulse"
/>
))}
</div>
</div>
);
}
animate-pulse Tailwind класс нь элементийг мигаруулж "skeleton" эффект үүсгэдэг. Хэрэглэгч хуудас руу шилжих мөчид энэ skeleton харагдаж, өгөгдөл ирмэгц бодит агуулгаар солигдоно.
loading.tsx хэрхэн ажилладаг вэ?
Next.js loading.tsx-г автоматаар Suspense boundary болгон ашигладаг. Дотроосоо ийм харагдана:
// Next.js дотооддоо ингэж хийдэг — өөрөө бичих шаардлагагүй
<Suspense fallback={<CoursesLoading />}>
<CoursesPage />
</Suspense>
page.tsx-н async функц өгөгдөл татаж дуустал loading.tsx харагддаг. Маш энгийн, маш хүчтэй!
React Suspense шууд ашиглах
Нэг хуудасны зарим хэсэг удаан, зарим нь хурдан ачаалдаг тохиолдолд <Suspense> шууд ашиглана. Ингэснээр хурдан хэсгүүд нь нэн даруй харагдаж, удаан хэсэг нь ачаалагдаж байна:
// app/courses/[courseSlug]/page.tsx
import { Suspense } from "react";
import LessonList from "@/components/lesson/LessonList";
import CourseStats from "@/components/CourseStats";
interface Props {
params: Promise<{ courseSlug: string }>;
}
export default async function CoursePage({ params }: Props) {
const { courseSlug } = await params;
return (
<main className="p-6">
{/* Энэ хэсэг шууд харагдана */}
<h1 className="text-2xl font-bold text-white">{courseSlug} курс</h1>
{/* CourseStats удаан татагддаг — дангаар нь Suspense-д оруулна */}
<Suspense fallback={<StatsLoading />}>
<CourseStats courseSlug={courseSlug} />
</Suspense>
{/* LessonList тусдаа ачаалагдана */}
<Suspense fallback={<LessonsLoading />}>
<LessonList courseSlug={courseSlug} />
</Suspense>
</main>
);
}
function StatsLoading() {
return (
<div className="flex gap-4 my-4">
{[1, 2, 3].map((i) => (
<div
key={i}
className="h-16 w-24 bg-[#1e293b] rounded-lg animate-pulse"
/>
))}
</div>
);
}
function LessonsLoading() {
return (
<div className="space-y-2 mt-4">
{Array.from({ length: 5 }).map((_, i) => (
<div
key={i}
className="h-12 bg-[#0f172a] border border-[#1e293b] rounded-lg animate-pulse"
/>
))}
</div>
);
}
Ийм байдлаар хуудасны хэсгүүд нь зэрэгцэн ачаалагдана — нийт хүлээх хугацаа богиносно.
Skeleton component дахин ашиглах
Skeleton UI-г дахин ашиглах боломжтой component болгож бичих нь зөв загвар:
// components/ui/LoadingSpinner.tsx
export default function LoadingSpinner() {
return (
<div className="flex items-center justify-center py-12">
<div className="w-8 h-8 border-2 border-[#1e293b] border-t-indigo-400 rounded-full animate-spin" />
</div>
);
}
// components/ui/SkeletonCard.tsx
export default function SkeletonCard() {
return (
<div className="bg-[#0f172a] border border-[#1e293b] rounded-xl p-5 animate-pulse">
<div className="h-4 w-16 bg-[#1e293b] rounded mb-3" />
<div className="h-6 w-3/4 bg-[#1e293b] rounded mb-2" />
<div className="h-4 w-full bg-[#1e293b] rounded" />
<div className="h-4 w-2/3 bg-[#1e293b] rounded mt-1" />
</div>
);
}
// app/courses/loading.tsx — Дахин ашиглаж болно
import SkeletonCard from "@/components/ui/SkeletonCard";
export default function CoursesLoading() {
return (
<div className="max-w-6xl mx-auto px-4 py-8 grid grid-cols-1 md:grid-cols-3 gap-4">
{Array.from({ length: 9 }).map((_, i) => (
<SkeletonCard key={i} />
))}
</div>
);
}
loading.tsx давхарлалт
Layout шиг loading.tsx мөн давхарлагдана. /courses/javascript хаягаар ороход дараах дарааллаар хайна:
app/courses/[courseSlug]/loading.tsx ← Эхлээд энд хайна
app/courses/loading.tsx ← Байхгүй бол энд
app/loading.tsx ← Байхгүй бол root-д
Хурдан ачаалагддаг хуудасд loading.tsx нэмэх шаардлагагүй — зөвхөн өгөгдөл татдаг удаан хуудсуудад л нэм.
Дараагийн хичээлд:
Error UI судална. error.tsx файл ашиглан сервер дээр гарсан алдааг хэрэглэгчид ойлгомжтой байдлаар харуулж, дахин оролдох боломж олгох аргыг ойлгоно.