Cách sử dụng hàm generateStaticParams trong NextJS 13. generateStaticParams nó được gọi tại thời điểm xây dụng ứng dụng, có nghĩa là khi ứng dụng chạy lên, nó sẽ được gọi trước. Để có dữ liệu rồi, nó sẽ Props params đến cho component xử lý hiển thị dữ liệu ra.
Mình có cấu trúc thư mục của project như sau:
Nhưng mình chia sẻ ngắn gọn về cách dung generateStaticParams thôi
+ File layout.tsx
import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' import Post from './post/page' const inter = Inter({ subsets: ['latin'] }) export const metadata: Metadata = { title: 'Create Next App', description: 'Generated by create next app', } export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="en"> <body className={inter.className}> <Post /> {children} </body> </html> ) }
Trong file layout.tsx mình có gọi Post component
Vì vậy ta cần tạo đường dẫn route : src/app/post/page.tsx. Dùng để hiển thị danh sách các bài viết ra
'use client' import Link from 'next/link' import useSWR from 'swr' const fetcher = (url:string) => fetch(url).then(res => res.json()) export default function Post() { const { data: posts, error, isLoading } = useSWR('/api/posts', fetcher) if (error) return <div>failed to load</div> if (isLoading) return <div>loading...</div> // render data return <div> <ul className='w-full grid grid-cols-4 gap-2 lg:grid-cols-4'> { posts.result && posts.result.data.map(({title,slug,content,id,image} : any )=>{ return( <li key = {id} className='p-2 bg-white shadow-md rounded-sm transtion-all ease-in-out duration-300 hover:scale-105'> <h3><Link href={`/post/${id}`} className='text-gray-600 py-2 capitalize text-[13px] block transtion-all ease-in-out delay-100 hover:text-secondary-900'>{title}</Link></h3> </li> ) }) } </ul> </div> }
Đoạn code của Post component ta có dùng thư viện swr. Thư viện swr rất hay, giúp ta có thể request các api một cách dễ dàng , đồng thời cũng dễ check dữ liệu được trả về thông qua các giá trị sau:{data, errors, isLoading}
Nếu như bạn có xem về RTK Query , thì cũng có thể hình dùng về cách gọi swr.
Bên trên do ta có gọi tới api/post, nên có nghĩa là ta cũng cần phải tạo route cho nó luôn nhé:
+ app/api/post/route.tsx : Dùng để trả về dữ liệu danh sách bài viết,
import { NextRequest,NextResponse } from 'next/server' export const dynamic = "force-static"; export async function GET(request: Request) { const { searchParams } = new URL(request.url) let page : any = searchParams.get('page'); if(page==null) page=1; const res = await fetch(process.env.PATH_URL_BACKEND+`/api/posts?page=${page}`, { headers: { 'Content-Type': 'application/json', //'API-Key': process.env.DATA_API_KEY, }, }) const result = await res.json() return NextResponse.json({ result }) } export async function POST(request: Request,response: NextRequest) { const { title } = await request.json() return NextResponse.json({title}) }
Okay giờ ta cần thực hiện
+ app/post/[id]/pages.tsx
import Image from 'next/image' async function getPost( id : number | string ) { console.log("->RUN 3",id); const res = await fetch(`http://127.0.0.1:8000/api/posts/${id}`); return res.json(); } export default async function Page({ params }: { params: { id: number | string } }) { console.log("->RUN 2",params); const data = await getPost(params.id); return ( <> <span>Title:{data.result.title}</span> </> ) } export async function generateStaticParams() { console.log("->RUN 1"); const posts = await fetch('http://127.0.0.1:8000/api/posts').then((res) => res.json()) return posts.data.map((post) => ({ id: post.id.toString() })) }
Okay vậy là xong, mình sẽ chạy nó, các bạn xem log nhe
+ Đầu tiền nó chạy hàm để fetch api lấy dữ liệu danh sách post, nó trả về đúng id bài post đó
generateStaticParams()
+ Sau khi chạy xong, nó sẽ gửi giá trị tham số id post về cho hàm Page component xử lý
function Page({ params }: { params: { id: number | string } })
+ Trong hàm Page nó sẽ đọc tham số và gọi api để lấy thông tin trả về->sau đó hiển thị dữ liệu ra cho người dùng xem
const data = await getPost(params.id); return ( <> <span>Title:{data.result.title}</span> </> )