Nextjs14初心者入門-Loading UIとStreaming
まず初めに
最近、Nextjs14の初心者本を書きました。そこでは書ききれなかった内容がまだまだあるので、強化拡張パックとして記事に落とし込むことにしました。
もしまだ下記の本を読んでいない方は、ぜひこちらから読んでみてください。
本を読んでいなくてもLoading UIとStreamingについてわかるようには書いています。
LoadingUI
app
ディレクトリの直下にloading.tsx
を作成しましょう。
src
└ app
└ loading.tsx
const Loading = () => {
return(
<div className="flex justify-center items-center gap-6 mt-10">
<div className="h-10 w-10 animate-spin border-[5px] border-sky-400 rounded-full border-t-transparent"></div>
<p className="text-[30px] font-weight">Loading</p>
</div>
)
}
export default Loading
また、blog
フォルダの中のpage.tsx
の中身をコンポーネント化しましょう。
waitTime
というpropsを受け取り、ローディング画面が見えやすいようにデータフェッチの時間を調整できるようにします。
src
└ app
├ blog
│ └ page.tsx
├ components
│ └ blogList.tsx
└ loading.tsx
import Loading from "@/app/loading";
import Link from "next/link"
import { Suspense } from "react";
interface TBlog {
id: string;
title: string;
content: string;
}
const getBlogData = async (waitTime: number) => {
//ルーディング画面がわかりやすくするために処理
await new Promise(resolve => setTimeout(resolve, waitTime))
const res = await fetch('http://localhost:3000/api/blog', {
cache: 'no-store'
})
const blogData = await res.json()
return blogData
}
const BlogList = async ({ waitTime }: { waitTime: number }) => {
const blogData = await getBlogData(waitTime)
return (
<div className="grid grid-cols-12 gap-3 mb-3">
{blogData.map((blog: TBlog) => (
<div className="col-span-4 border border-black rounded p-5" key={blog.id}>
<Link href={`/blog/${blog.id}`} className="w-full">
<h2>{blog.title}</h2>
</Link>
</div>
))}
</div>
)
}
export default BlogList
import BlogList from "@/app/components/blogList";
import Loading from "@/app/loading";
const BlogPage = async () => {
return (
<div className="container mx-auto py-[50px]">
<h2 className="text-[50px] font-bold mb-5">Blog</h2>
<BlogList waitTime={3000} />
</div>
)
}
export default BlogPage
/blog
でページをリフレッシュしましょう。
すると先ほど作成したローディング画面が表示されます。
loading.tsx
はNextjsにおいて特別な意味を持つファイルで、データフェッチなどの際にローディング画面を差し込むことができます。
Streaming
pattern1
blogList
コンポーネントをSuspense
コンポーネントでラップし、fallbackにLoading
コンポーネントを渡しておきましょう。
import BlogList from "@/app/components/blogList";
import Loading from "@/app/loading";
import { Suspense } from "react";
const BlogPage = async () => {
return (
<div className="container mx-auto py-[50px]">
<h2 className="text-[50px] font-bold mb-5">Blog</h2>
<Suspense fallback={<Loading />}>
<BlogList waitTime={3000} />
</Suspense>
</div>
)
}
export default BlogPage
/blog
でリフレッシュしましょう。
Suspense
コンポーネントでラップすることによって、ラップされたコンポーネントの部分がデータフェッチをすることを待たずして、他のUI部分を表示することができ、結果的にユーザー体験を向上させることができます。
pattern2
次にBlogList
コンポーネントを複製し、複製した方のwaitTime
を5000としましょう。
import BlogList from "@/app/components/blogList";
import Loading from "@/app/loading";
import { Suspense } from "react";
const BlogPage = async () => {
return (
<div className="container mx-auto py-[50px]">
<h2 className="text-[50px] font-bold mb-5">Blog</h2>
<BlogList waitTime={3000} />
<BlogList waitTime={5000} />
</div>
)
}
export default BlogPage
/blog
でページをリフレッシュしましょう。
Suspense
でラップしないと、全てのデータフェッチが完了してからUIが表示されます。
次に各BlogList
コンポーネントをSuspense
コンポーネントでラップして、fallbackにLoading
コンポーネントを渡しましょう。
import BlogList from "@/app/components/blogList";
import Loading from "@/app/loading";
import { Suspense } from "react";
const BlogPage = async () => {
return (
<div className="container mx-auto py-[50px]">
<h2 className="text-[50px] font-bold mb-5">Blog</h2>
<Suspense fallback={<Loading />}>
<BlogList waitTime={3000} />
</Suspense>
<Suspense fallback={<Loading />}>
<BlogList waitTime={5000} />
</Suspense>
</div>
)
}
export default BlogPage
/blog
でページをリフレッシュしましょう。
データフェッチが完了したUIから表示されます。
まとめ
他にも本には書ききれなかった内容がたくさんあるので、強化拡張パックという形で書いていきたいと思います。ここまで読んでいただきありがとうございました。
普段は、Reactを中心にWebのフロントエンドに関する発信をしています。他にも下記の本を書いているので興味があれば、ぜひ読んでみてください。
参考資料
Discussion