- Next.js คืออะไร? + มาหัดเขียนเว็บด้วย Next.js กันดีกว่า
- ตอนที่ 1 - Hello Next.js เริ่มสร้างโปรเจ็ค
- ตอนที่ 2 - สร้าง Page และการ navigate ระหว่าง Pages
- ตอนที่ 3 - การจัดการ Assets, Metadata และ CSS
- ตอนที่ 4 - เรื่องของ Pre Rendering
- ตอนที่ 5 - Data Fetching และการดึงข้อมูลจาก API
- ตอนที่ 6 - การทำ Dynamic Routes
- ตอนที่ 7 - API Routes การทำ API ด้วย Next.js
ตอนที่ 6 - การทำ Dynamic Routes
เขียนวันที่ : Jul 16, 2022
ปกติแล้ว Routing ใน Next.js จะเป็นแบบ file-system เพียงแค่เราสร้างไฟล์ในโฟลเดอร์ pages
เราก็จะได้หน้าเว็บนั้นๆ เช่น สร้างไฟล์ about.js
url เราก็จะเป็น /about
ทีนี้ถ้าเรามีเว็บ ที่เป็น Blog หรือระบบสินค้า ที่ต้องมี Product สมมติ มี 50ตัว ต้องมาสร้างไฟล์ 50 ไฟล์ ไว้ใน pages
ก็ดูจะเป็นงานที่ไม่น่าทำนัก โชคดีที่ Next.js รองรับการทำ Dynamic Route ครับ
Dynamic Page
คือการกำหนด Pattern ให้ Route ของเราเช่น /blog/:id
หรือ /products/:id
ซึ่ง ตัว :id
จะเป็นอะไรก็ได้ แล้วแต่เรา ข้อดีคือ เราใช้แค่ไฟล์เดียว
โดยตัว :id
เราเรียกมันว่า param
ฉะนั้น สำหรับ Next.js เราก็แค่สร้างไฟล์ชื่อว่า [id].js
ตัวอย่างเช่น ผมสร้างไฟล์ products/[id].js
ใน pages
import { useRouter } from 'next/router'
const Blog = () => {
const router = useRouter()
const { id } = router.query
return <p>Blog ID : {id}</p>
}
export default Blog
ทีนี้ไม่ว่า url เราจะเป็น /blog/1
, /blog/100
หรืออะไรก็แล้วแต่ เราก็จะได้หน้าเว็บที่แสดง Blog ID ที่แตกต่างกัน ตามที่เราต้องการ ตัว Next.js สามารถอ่าน query.id
(ชื่อเดียวกับไฟล์ ที่ตั้งด้วย []
)
Catch-all Routes
ตัว Dynamic routes เรายังสามารถ ดักจับ path ทั้งหมด ได้ด้วยการใช้ 3จุด (...
) ข้างใน []
ตัวอย่างเช่น
pages/products/[...id].js
จะ match ทั้ง/products/1
,/products/1/2/3
,/products/1/detail
อะไรก็แล้วแต่ เรียกได้ว่า เข้าเงื่อนไขหมด
ทดลองสร้างไฟล์ขึ้นมา สมมติชื่อ [...id].js
ไว้ในโฟลเดอร์ products
ข้างใน pages
import { useRouter } from 'next/router'
const Product = () => {
const router = useRouter()
const { id } = router.query
return <p>Product ID : {id?.join(' - ')}</p>
}
export default Product
ซึ่งถ้าเราเรียก url /products/1/2
ตัว id
เราก็จะมีค่า
id: ['1', '2']
อ่านเพิ่มเติมเรื่อง Dynamimc Routes
Fetch API + Dynamic Routes
ตัวอย่าง ลองทำ Dynamic Routes และแต่ละหน้า ก็ทำการ fetch api ที่แตกต่างกัน เช่น
/posts/1
ก็ให้ fetch apihttps://jsonplaceholder.typicode.com/posts/1
/posts/2
ก็ fetchhttps://jsonplaceholder.typicode.com/posts/2
โดยที่เราไม่ต้องมาเขียนโค๊ดทุกๆไฟล์
เริ่มต้น สร้างไฟล์ posts/[id.js]
ขึ้นมา
const Post = ({ post }) => {
return (
<div>
<h2>{post.title}</h2>
<p>{post.body}</p>
</div>
)
}
export async function getStaticProps({ params }) {
const id = params.id
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
const post = await res.json()
return {
props: {
post,
},
}
}
export default Post
จากโค๊ด จะเห็นว่าเราใช้ params
จาก getStaticProps
เพื่อ fetch /posts/1
, /posts/2
ขึ้นอยู่กับ url ทีนี้เมื่อเราลองรัน yarn dev
เราจะเจอ error ประมาณนี้
Server Error
Error: getStaticPaths is required for dynamic SSG pages and is missing for '/posts/[id]'.
Read more: https://nextjs.org/docs/messages/invalid-getstaticpaths-value
คือโหมด SSG เราจำเป็นต้องใช้ getStaticPaths
getStaticPaths
getStaticPaths
ต้อง return object เพื่อบอกว่าจะให้ path (url) ไหน ที่ทำการ pre-render บ้าง ซึ่ง object ก็จะอยู่ในรูปแบบนี้
return {
paths: [{ params: { id: 1 }}, { params: { id: 2 }}],
fallback: false
}
ซึ่งถ้า getStaticPaths
return paths มาแบบนี้ ตัว Next.js ก็จะรู้ละว่ามี params
1 กับ 2 ก็จะทำการ generate /posts/1
และ /posts/2
ให้ เวลาเรา next build
โดยใช้ Page component ไฟล์ /pages/posts/[id].js
นั่นเอง ถ้า page เราชื่อ /pages/posts/[postId].js
ตัวที่ return ก็ต้องเป็น path: [{ params: { postId: 1 }}]
fallback: false
- เป็นการบอกว่า ถ้ามันไม่เจอ path เช่น/posts/1000
มันก็จะทำการ return 404 Page
กลับไปที่ก่อนหน้านี้ ที่ไฟล์ posts/[id.js]
ทำการเพิ่ม getStaticPaths
ให้มัน แบบนี้
export async function getStaticPaths() {
const url = `https://jsonplaceholder.typicode.com/posts`
const res = await fetch(url)
const posts = await res.json()
const paths = posts.map((post) => {
return {
params: { id: String(post.id) },
}
})
return {
paths,
fallback: false,
}
}
จะเห็นว่า เราไม่ต้องมากำหนด paths: params
ทีละตัวครับ เราใช้วิธี fetch data เพื่อได้ posts ทั้งหมด จากนั้น loop เอาแค่ id มา เมื่อได้ id ก็กลายเป็น params: {id}
นั่นเอง (สังเกตผมใช้ String(id)
เพื่อเปลี่ยนจาก number เป็น string)
ทดสอบรัน yarn dev
และลองเข้าหน้าเว็บดูใหม่
- http://localhost:3000/posts/1
- http://localhost:3000/posts/2
- http://localhost:3000/posts/9999 (จะเจอ 404 เนื่องจากไม่มี posts นี้)
จบแล้วสำหรับเรื่อง Dynamic Routes ตอนต่อไป เป็นเรื่อง API Routs ต่อนะครับ
คำถาม
fallback
ในgetStaticPaths
มีกี่แบบ?- ถ้าเรากำหนด catch-all routes
[...id].js
เราจะใช้getStaticPaths
ตัวparams
จะเป็นแบบไหน?number
,string
,array
? - catch-all routes เป็น optional ได้มั้ย?