สอนเขียนเว็บด้วย React.js ปี 2023 (Intro to React) ตอนที่ 3
สวัสดีครับ บทความนี้บทความที่ 3 และเป็นตอนสุดท้ายของซีรีย์ สอนเขียนเว็บด้วย React แล้วนะครับ
สำหรับตอนนี้ จะเป็นเรื่องของการใช้ React Router เพื่อทำเว็บให้รองรับหลายๆหน้า และก็การ Deploy เว็บไซต์ของเราด้วย Netlify นะครับ
เนื้อหาสอนเขียนเว็บ React.js มีทั้งหมด 3 ตอนนะครับ
- ตอนที่ 1 - React เบื้องต้น เช่น Component, JSX, Props, State และ Events
- ตอนที่ 2 - React Form, useEffect, fetchAPI, React Hooks, Custom Hooks
- ตอนที่ 3 - React Router และการ Deployment
1. React Router
ปกติตัว React เป็น Client และตัวไฟล์ ก็มีเพียงแค่ index.html
เราจะทำหน้าเว็บให้มีหลายๆหน้ายังไง? คำตอบคือใช้ Client Router ซึ่งที่นิยมก็หนีไม่พ้น React Router ครับ
ตัว React Router ทำให้เราสามารถที่จะกำหนด route หน้าต่างๆ ว่าเว็บเราจะมีหน้าอะไรบ้าง (เป็นฝั่ง Client อย่างเดียวนะครับ ตัว Server ก็ยังคงมีแค่ index.html
)

หลักการทำงานของ ClientRouter
ปกติ เวลาเรา Users (Client) จะ request ไปที่ Server ตัว Server ก็จะส่งข้อมูลกลับใช่มั้ย
users -> request -> server
server -> response -> index.html
เมื่อฝั่ง Users ได้รับข้อมูล ใน index.html
ก็จะมีไฟล์ React bundle js อยู่ เวลาที่ user กดหน้าต่างๆ มันก็จะมาเข้าเงื่อนไขใน javascript เช็คว่า router เราตั้งค่าไว้ถูกมั้ย ถ้าถูกก็ render react ตามหน้าที่เรากำหนดไว้
สำหรับตัวอย่างนี้ ผมก็จะเริ่มสร้างโปรเจ็คขึ้นมาใหม่เลย โดยใช้ Vite
npm create vite@latest hello-react-router -- --template react-ts
ต่อมาทำการติดตั้ง React Router Dom
npm install react-router-dom
ทีนี้สิ่งที่เราจะทำคือ เราต้องการให้เว็บเรามี 3 หน้า หน้าคือ
/
- คือหน้า Home Page/products
- เป็นหน้า Products/about
- เป็นหน้า About Page
ใน React Router เราสามารถทำ Routes ได้หลายวิธี โดยตัวอย่าง ผมจะยกมา 2 แบบคือ
1. ใช้ Routes และ Route
เริ่มจาก ใช้ BrowserRouter
โดยการเปิดไฟล์ main.tsx
ให้ <App />
อยู่ภายใต้ BrowserRouter
แบบนี้
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { BrowserRouter } from 'react-router-dom'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
)
จากนั้นกำหนด Routes และ Route ที่ไฟล์ App.tsx
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="products" element={<Products />} />
<Route path="*" element={<p>Not found</p>} />
</Route>
</Routes>
หลักการทำงานคือ เราจะใช้ Routes
เป็นเหมือน Provider ครอบ route ที่เราต้องการทั้งหมด
จากนั้น <Route>
จะกำหนด
path
- หมายถึง url path ที่ match กับ browser urlelement
- เมื่อ path match กัน มันจะทำการ render component ที่เรากำหนด
อย่างเช่น ตัวอย่าง
- ถ้าเราเข้าเว็บ http://localhost:5173 มันจะก็จะ match path
/
และทำการ render Layout - แต่ถ้าเราเข้า http://localhost:5173/about มันก็จะ match path
about
ก็ render About นั่นเอง
ทีนี้มาดูที่ component Layout เราทำการสร้าง Layout.tsx
ขึ้นมา ผมสร้างไว้ใน folder components
import { Link, Outlet } from 'react-router-dom'
export default function Layout() {
return (
<>
<header>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/products">Products</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
</nav>
</header>
<main>
<Outlet />
</main>
<footer>React Router Example - 2023</footer>
</>
)
}
จะสังเกตเห็น <Outlet />
จะทำหน้าที่คล้ายๆการ render children
(ไม่เหมือนกันซะทีเดียว) คือจะทำการ render nest Router ข้างใน ถ้าเรากลับไปดู routes ที่เรากำหนดไว้
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="products" element={<Products />} />
<Route path="*" element={<p>Not found</p>} />
</Route>
</Routes>
เมื่อ path เป็น about
ตัว <Outlet>
ก็จะถูกแทนที่ด้วยการ render <About />
นั่นเอง
2. Router Providers
วิธีนี้จะใช้แบบ RouterProvider โดยการเรียก createBrowserRouter()
วิธีนี้ ข้อดีคือมีพวก Data API ที่มากับ React Router v6.4 (เช่นพวก data loader, fetcher, actions ต่างๆ)
วิธีการคล้ายๆ แบบแรก แต่เปลี่ยนจาก <BrowserRouter>
มาเป็น <RouterProvider>
แบบนี้
import { RouterProvider } from 'react-router-dom'
<RouterProvider router={router} />
โดยที่ router
สร้างจาก createBrowserRouter()
โดยใช้ config ก็คือ routes ที่เราต้องการ
import { createBrowserRouter } from 'react-router-dom'
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{
path: '/',
index: true,
element: <Home />,
},
{
path: 'about',
element: <About />,
},
{
path: 'product',
element: <Product />,
},
{
path: '*',
element: <p>Not Found</p>,
},
],
},
])
การ navigate หรือการ link ไปหน้าต่างๆ
ในการทำ link ต่างๆ ใน React Router เราจะใช้ <Link>
ดังที่ได้เห็นในโค๊ดตัวอย่าง ตรงที่เป็น Layout.tsx
เช่น <Link to="">
import { Link } from 'react-router-dom'
function IndexPage() {
return (
<div>
<h1>Hello React Router</h1>
<Link to="/about">About</Link>
</div>
);
}
- ใช้ attribute (props)
to
ซึ่งถ้าเป็น<a>
เราจะใช้href
นั่นเอง
การทำ Dynamic Route
ใน React Router เราสามารถกำหนด pattern ให้ dynamic route ได้ เช่น เรามี product เยอะๆ สมมติ 100 ตัว การที่เราต้องมากำหนด route ให้ 100 ตัว คงลำบากน่าดู และไม่น่ามีใครเค้าทำกันใช่มั้ยครับ 🤣
<Route path="products/1" element={<Product1 />} />
<Route path="products/2" element={<Product2 />} />
<Route path="products/3" element={<Product3 />} />
...
<Route path="products/100" element={<Product100 />} />
เราสามารถกำหนด pattern ได้โดยใช้ colon (:
) แบบนี้ได้ครับ
<Route path="products/:id" element={<ProductDetail />} />
แล้วเราจะ get ค่า :id
ได้ยังไง? เราดึงได้จาก params.id
ครับ ใช้ useParams()
สร้าง component ProductDetail
ขึ้นมา
import { useParams } from 'react-router-dom'
export default function ProductDetail() {
const { id } = useParams()
return (
<div>
<h2>Product ID: {id}</h2>
</div>
)
}
ทีนี้เวลาเราเข้า url ที่มี id ต่างกัน เราก็จะได้ Product ID ต่างกันแล้วครับ
2. Deployment
เรื่องสุดท้าย เรื่องของการ Deployment Project ของเรา ปกติเวลาที่เรา dev เรารันคำสั่ง
npm run dev
เวลาแก้ไขไฟล์มันจะทำการ refresh การเปลี่ยนแปลงให้เอง เราอาจจะไม่รู้ว่าไฟล์ที่เราจะต้องเอาไป deploy เป็นยังไง ต้องอัพ component ลงไปที่ Hosting ที่เราจะใช้หรือไม่?
จริงๆ แล้ว การ deploy ก็เหมือนกับเรา deploy เว็บไซต์ HTML/CSS/JS ปกติเลยครับ คือมีแค่ไฟล์ html ไฟล์ css และ ไฟล์ javascript
ทีนี้ การที่เราจะ build เพื่อไป deploy เนี่ย จะใช้คำสั่งนี้
npm run build
ซึ่งคำสั่งนี้ตัวที่เราใช้ Vite มันคือการ compile typescript จากนั้น ก็ทำการ build ไฟล์ให้เรา โดยการ minify & bundle ไฟล์ต่างๆ ที่เราเขียนพวก components ต่างๆ ให้เป็นไฟล์ bundle ไฟล์เดียว และ output อยู่ใน folder dist
ซึ่งถ้าเราดูใน folder จะมีไฟล์อยู่แค่ html, css และ js (ชื่อไฟล์ต่างกันตาม hash ที่ random generate)
├── assets
│ ├── index-04490b11.css
│ └── index-2bb88b1a.js
├── index.html
└── vite.svg
การ deploy ก็เพียงแค่อัพไฟล์ พวกนี้ลงบน Hosting ของเรา เราก็ได้หน้าเว็บแล้วครับ ก่อน deploy เรามาลองรัน localhost ด้วยการใช้ npx serve
ดูครับ เป็นเหมือนการจำลอง server ขึ้นมา เพื่อ serve ไฟล์ใน dist
ครับ
npx serve dist
จากนั้น ลองเข้าเว็บ http://localhost:3000 และลองเล่นดู จะเห็นว่าเราสามารถกด link อะไรได้ปกติ แต่ว่า ถ้าสมมติ เรา เข้า url http://localhost:3000/products/100 โดยการ refresh มันจะหาหน้าเว็บไม่เจอ
ซึ่งถูกต้องแล้วครับ ใน folder dist
เราไม่มีไฟล์ products/100.html
ซึ่งทั้งหมดมันปกติครับ เพราะว่า React Router มันทำงานฝั่ง client มันไม่ได้สร้างไฟล์ พวกนี้ วิธีการแก้ไข หรือปกติที่ทำกันคือ
เวลาที่เรากำหนด web server (apache, nginx) ส่วนใหญ่ เค้าจะทำการ rewrite url พวก not found ทั้งหลาย หรือทุกๆ url ให้ไปที่ index.html
ครับ
ส่วนตัว serve
ทำได้โดยการใช้ option -s
แบบนี้
npx serve -s dist
Deploy ด้วย Netlify

การ Deploy ด้วย Netlify ที่ง่ายที่สุด คือ จับโยน folder dist
ลงไปเลย โดยเราเลือก Add a new site -> จากนั้นเลือก Deploy Manually

จะไปหน้านี้ จากนั้นเราก็ upload folder dist ของเราลงไปเลย
เมื่ออัพโหลดไฟล์ของเราเสร็จ ตัว Netlify ก็จะ generate website ให้เรา เราก็จะได้เว็บของเราแล้วครับ (สามารถเปลี่ยนชื่อ subdomain ทีหลังได้ ถ้าชื่อไม่ซ้ำกับคนอื่น)
นอกจากนี้ ก็ยังมีวิธีการ deploy โดยเชื่อมกับ Git นะครับ ทุกครั้งที่เรา push code ลงไป ตัว Netlify ก็จะทำการ build และ deploy ให้เรา แต่ขั้นตอนนี้ผมไม่ได้เขียนนะครับ ลองไปทำกันดูนะครับ
ต่อมา ตัว Netlify ก็มีปัญหาเช่นกัน คือมันไม่ได้ rewrite url ต่างๆ ถ้าสมมติเราเข้า /products/50 มันจะไม่เจอ สิ่งที่เราต้องทำคือ สร้างไฟล์ _redirects
ขึ้นมา ในโฟลเดอร์ dist
นั่นแหละ ข้างในเป็นแบบนี้
/* /index.html 200
เป็นการกำหนดว่าให้ทุกๆ url ที่เข้ามา ให้มันไป /index.html
ให้หมด (เพื่อให้ client routing มันทำงาน)
จากนั้นก็ upload dist ไปทับของเก่า และลองทดสอบเว็บอีกครั้งครับ, ตัวเลือกอื่นๆในการ Deploy เช่น Vercel, Cloudflare, Github Pages, Firebase Hosting
congrats 🎉🎉🎉🎉
Source Codeสุดท้าย ผมขอฝากคำถาม จริงๆ เป็นกึ่งๆ คำถาม กึ่งๆ quiz ให้ลองไปฝึกทำเพิ่มเติมกันดูครับ
- ลองทำเว็บ Portfolio โปรโมทตัวเอง สร้างกี่หน้าก็ได้ ตามใจเลย
- ปรับแต่ง CSS ตามใจชอบ
- อยากใช้ Bootstrap, MUI หรือ Tailwind CSS ตั้งค่ายังไง?
- ลอง fetch data มาแสดงผลได้มั้ย?
- ลองใช้แบบ
useEffect()
หรือลองloader
ของ React Router? - ถ้าจะ Deploy ด้วย Netlify แบบใช้ Git ทำยังไง?
- หากเราจะทำ custom domain เอง ทำได้มั้ย?
- ถ้าเราไม่ชอบ Netlify อยากใช้ตัวเลือกอื่นเช่น Vercel ทำได้มั้ย ทำยังไง?
หลังจากเรียนจบคอร์สนี้แล้ว แม้ว่าคอร์สนี้จะไม่ได้สอนทุกอย่าง และบางเรื่องก็เป็นพื้นฐานเท่านั้น แต่ก็คิดว่าเพียงพอให้นำความรู้ที่ได้ไปต่อยอดเพื่อเติมได้ สิ่งสำคัญคือการฝึกฝน และลงมือเขียนครับ แค่อ่าน หรือดู Video เพียงอย่างเดียว คงไม่พอ
ใครที่เรียนจบแล้ว มาถึงตรงนี้ ลองทำเว็บของตัวเองขึ้นมา และก็ลอง Deploy เอา link มาแชร์กันได้นะครับ ที่ Discord หรือ ส่ง link Github เพื่อให้ผมหรือเพื่อนๆใน Community ช่วย Review ได้ครับ
หากใครมีคำถาม ติดปัญหา หรือไม่เข้าใจตรงไหน สามารถสอบถาม หรือมีข้อแนะนำ ข้อเสนอแนะ สามารถพูดคุยกันได้นะครับ
Happy Coding ❤️