Devahoy Logo
PublishedAt

Next.js

เขียนเว็บด้วย Next.js + TypeScript ตอนที่ 2 - ว่าด้วยเรื่อง Routing และ Dynamic Routes

เขียนเว็บด้วย Next.js + TypeScript ตอนที่ 2 - ว่าด้วยเรื่อง Routing และ Dynamic Routes

สวัสดีครับ มาต่อกันที่ ตอนที่ 2 วันนี้จะเป็นเรื่องของ Routing และ Dynamic Routes ครับ วันนี้จะเป็นเนื้อหาเพิ่มเติมของตอนที่ 1 เกี่ยวกับการสร้างหน้าเพจ และ Route

เขียนเว็บด้วย Next.js + TypeScript ตอนที่ 1

อย่างที่ยกตัวอย่างคร่าวๆ ในตอนที่ 1 ไปเรื่องของการสร้าง Pages ใน Next.js

  • เมื่อเราเพิ่มไฟล์ในโฟลเดอร์ pages มันก็จะเป็น routes ให้อัตโนมัติ
  • ก่อนหน้านี้เราสร้างหน้า About ก็คือเพิ่มไฟล์ pages/about.tsx จากนั้น routes ก็จะถูก generate เป็น /about ให้

Index Routes

เราสามารถกำหนดชื่อไฟล์เป็น index.tsx ก็ได้ หรือมีโฟลเดอร์หลายชั้นก็ได้ ตัว routes จะถูกแปลงเป็นแบบตัวอย่างนี้

  • pages/index.tsx/
  • pages/blog/index.tsx/blog
  • pages/about/index.tsx/about

Nested Routes

จะทำ routing หลายๆชั้นก็ทำได้ เช่น

  • pages/blog/first-post.tsx/blog/first-post
  • pages/dashboard/settings/username.tsx/dashboard/settings/username

Dynamic Routes

รองรับการทำ Dynamic Routes สมมติเราทำเว็บมีสินค้าในระบบ 1000 ชิ้น ต้องมาเขียนไฟล์ 1000 ไฟล์ ไม่ไหวแน่ๆ ตัว Next ก็รองรับ dynamic route แบบนี้

  • ตั้งชื่อไฟล์หรือโฟลเดอร์ด้วย [] เช่น [id].tsx หรือ โฟลเดอร์ [users]
  • pages/products/[id].tsx/products/:id - เช่น /products/1 , /products/5
  • pages/post/[...all].tsx/post/\* (/post/2020/id/title)

ข้อดีคือ เราใช้แค่ไฟล์เดียว ที่ match กับ :id ที่เราต้องการ

เรื่องของ Dynamic Routes

เราจะมาลงลึกในส่วน Dynamic Routes กันครับ เริ่มจาก เราจะกำหนดให้เว็บเรามี product เป็น url แบบนี้

  • /products/:id เช่น /products/1 , /products/2

สิ่งที่เราต้องทำ ก็คือ สร้างไฟล์ [id].tsx ขึ้นมา ในโฟลเดอร์ pages/products

1
const ProductDetail = () => {
2
return <h1>This is Product Detail Page</h1>
3
}
4
5
export default ProductDetail

เราสามารถ get params จาก [id] ได้ด้วยการใช้ next/router

1
import { useRouter } from 'next/router'
2
3
const ProductDetail = () => {
4
const router = useRouter()
5
const { id } = router.query
6
7
return <h1>This is Product {id}</h1>
8
}
9
10
export default ProductDetail

จากโค๊ดด้านบน ตัว Router จะ get object จาก params ที่เราตั้งชื่อไว้แบบเดียวกับชื่อไฟล์ นอกจากนี้ ตัว Router ยัง get query parameters ด้วยนะครับ ตัวอย่าง pages แบบต่างๆ จะได้ค่า query ยังไงบ้าง?

  • pages/products/[id]/[comment].tsx -> pages/products/1/my-comment
Terminal window
{ "id": "1", "comment": "my-comment" }
  • /pages/products/100?sku=test
Terminal window
{ "id": "100", "sku": "test }

ทีนี้เราลองทดสอบด้วยการ start dev server ขึ้นมา แล้วลองเข้าเว็บ ด้วย url ต่างๆ จะเห็นค่า product id ที่แตกต่างกัน

Terminal window
npm run dev

Catch all routes

ตัว Dynamic routes เรายังสามารถ ดักจับ path ทั้งหมด ได้ด้วยการใช้ 3จุด (…) ข้างใน [] ตัวอย่างเช่น

  • pages/posts/[...all].js จะ match ทั้ง /posts/1, /posts/1/2/3, /posts/1/comments อะไรก็แล้วแต่ ที่ตามหลัง /posts จะเข้าเงื่อนไขทั้งหมด
Terminal window
// เช่น path /posts/1/comments/1000
const { all } = router.query
// all: ['1', 'comments', '1000']

ในตอนที่ 1 พูดไปแล้ว เรื่องการใช้ next/link ตัวอย่างก่อนหน้านี้

1
<Link href="/products/1">Go to Product 1</Link>

เราอยากใส่ Link อะไรก็แค่เปลี่ยน href ใช่มั้ยครับ หรือถ้าจะส่ง query string ไปด้วย ก็เป็น href=“/products/1?color=red&size=m ก็ทำได้

หรือ เราจะส่งแบบ URL Object แบบนี้ก็ได้ ไม่ต้องนำ query มาต่อ url ให้วุ่นวาย

1
<Link
2
href={{
3
pathname: '/products/[id]',
4
query: { id: 1 }
5
}}
6
>
7
Product #1
8
</Link>
9
10
<Link
11
href={{
12
pathname: '/products/[id]',
13
query: { id: 1, color: 'red', size: 'm' }
14
}}
15
>
16
Product #1
17
</Link>

ลงมือโค๊ดดีกว่า

ลงมือทำดีกว่า เพื่อให้เห็นภาพจริงๆ เริ่มจาก ลองทำหน้าขึ้นมา หน้า 1 เป็นหน้า Product List ครับ ผมตั้งชื่อว่า pages/products/index.tsx มีข้อมูล mockProducts ใส่ไปด้วย เป็น ดังนี้

1
import Link from 'next/link'
2
3
const mockProducts = [
4
{
5
id: 1,
6
title: 'Super cool T-Shirt',
7
size: 'm',
8
color: 'red',
9
},
10
{
11
id: 2,
12
title: 'Hello T-Shirt',
13
size: 'l',
14
color: 'black',
15
},
16
{
17
id: 3,
18
title: 'Ahoy Hoodie',
19
size: 'l',
20
color: 'black',
21
},
22
]
23
24
interface Product {
25
id: number
26
title: string
27
size: string
28
color: string
29
}
30
31
export default function Products() {
32
return (
33
<div>
34
<h2>My Products</h2>
35
36
<ul>
37
{mockProducts.map(({ id, title, size, color }: Product) => {
38
return (
39
<li key={id}>
40
<Link
41
href={{
42
pathname: '/products/[id]',
43
query: { id, size, color },
44
}}
45
>
46
{title}
47
</Link>
48
</li>
49
)
50
})}
51
</ul>
52
</div>
53
)
54
}

จากนั้นสร้างอีกไฟล์ สำหรับ Product Detail ชื่อ pages/products/[id].tsx ตอนนี้เราจะมี 2 ไฟล์ ใน โฟลเดอร์ pages/products แล้วนะครับ คือ

  • index.tsx - หน้า Products รวม จะเป็น route → /products
  • [id].tsx - หน้า Detail จะเป็น route →/products/:id

ไฟล์ products/[id].tsx จะมีข้อมูลดังนี้ แค่แสดงข้อมูล id จาก router

products/[id].tsx
1
import { useRouter } from 'next/router'
2
import Link from 'next/link'
3
4
const ProductDetail = () => {
5
const router = useRouter()
6
const { id, color, size } = router.query
7
8
return (
9
<div>
10
<h1>This is Product {id}</h1>
11
<p>Color : {color}</p>
12
<p>Size : {size}</p>
13
<Link href="/products">List Products</Link>
14
</div>
15
)
16
}
17
18
export default ProductDetail

ทดลอง start dev server ดูผลลัพธ์

Terminal window
npm run dev

🎉 Done!

จบไปแล้วครับสำหรับตอนที่ 2 ไม่ได้เน้นหน้าตา UI นะครับ เน้นให้เข้าใจการทำงานของ Routes และ Dynamic Routes รวมถึงการอ่านค่าจาก router params เป็นต้น

Authors
avatar

Chai Phonbopit

เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust

Related Posts