⚛️ บทที่ 3: Server Components vs Client Components
เรียนรู้ความแตกต่างและการใช้งาน Server Components และ Client Components ใน Next.js 15 + React 19
วัตถุประสงค์การเรียนรู้
เข้าใจความแตกต่างระหว่าง Server Components และ Client Components
รู้ว่าเมื่อไหร่ควรใช้ Server Components หรือ Client Components
สามารถสร้างและใช้งาน Server Components พร้อม React 19
เข้าใจการทำงานของ hydration และ React 19 concurrent features
🤔 React Server Components คืออะไร?
React Server Components เป็นฟีเจอร์ใหม่ที่ให้เราสามารถเรนเดอร์ React components บนเซิร์ฟเวอร์ได้ โดยไม่ต้องส่ง JavaScript ไปยังเบราว์เซอร์ ทำให้แอปพลิเคชันเร็วขึ้นและมี bundle size ที่เล็กลง
Server Components
เรนเดอร์บนเซิร์ฟเวอร์ และส่งผลลัพธ์ที่เป็น HTML ไปยังเบราว์เซอร์
✅ ไม่มี JavaScript ส่งไป client
✅ เข้าถึงฐานข้อมูลได้โดยตรง
✅ SEO-friendly
❌ ไม่รองรับ interactivity
Client Components
เรนเดอร์บนเบราว์เซอร์ เหมือนกับ React components แบบปกติ
✅ รองรับ interactivity เต็มรูปแบบ
✅ ใช้ React hooks ได้
✅ เข้าถึง browser APIs ได้
❌ ส่ง JavaScript ไป client
📊 เปรียบเทียบรายละเอียด
คุณสมบัติ | Server Components | Client Components |
---|---|---|
ที่ทำงาน | เซิร์ฟเวอร์ | เบราว์เซอร์ |
JavaScript Bundle | ไม่ส่งไปยัง client | ส่งไปยัง client |
State Management | ไม่รองรับ | รองรับ (useState, useEffect) |
Event Handlers | ไม่รองรับ | รองรับ (onClick, onChange) |
Browser APIs | ไม่เข้าถึงได้ | เข้าถึงได้ (localStorage, window) |
Data Fetching | ใช้ async/await โดยตรง | ใช้ useEffect + fetch |
SEO | ดีมาก (เรนเดอร์ที่เซิร์ฟเวอร์) | ต้องการ hydration |
Performance | Bundle เล็ก, โหลดเร็ว | Bundle ใหญ่กว่า |
🎯 เมื่อไหร่ควรใช้อันไหน?
🖥️ ใช้ Server Components เมื่อ:
การดึงข้อมูลจาก API (React 19)
ดึงข้อมูลจากฐานข้อมูลหรือ external API ด้วย async/await ใน React 19
ตัวอย่าง: รายการสินค้า, ข้อมูลผู้ใช้, บทความ พร้อม enhanced caching
การแสดงข้อมูลคงที่
แสดงข้อมูลที่ไม่ต้องการ interaction พร้อม Static Route Indicator
ตัวอย่าง: หน้า About, Footer, Header (จะมี static indicator ใน dev)
การประมวลผลข้อมูล
คำนวณหรือประมวลผลข้อมูลที่ซับซ้อนด้วย React 19 Server Components
ตัวอย่าง: การคำนวณราคา, สถิติ, รายงาน ด้วย async functions
การเข้าถึงฐานข้อมูล
เชื่อมต่อและดึงข้อมูลจากฐานข้อมูลด้วย enhanced caching
ตัวอย่าง: Prisma queries, MongoDB operations (ไม่ cache by default ใน Next.js 15)
Hydration และการแก้ปัญหา
🔄 กระบวนการ Hydration
1. Server Rendering
เซิร์ฟเวอร์สร้าง HTML จาก Server Components และส่งให้เบราว์เซอร์
2. HTML Display
เบราว์เซอร์แสดง HTML ทันที (ผู้ใช้เห็นเนื้อหาได้แล้ว แต่ยังกดไม่ได้)
3. JavaScript Loading
เบราว์เซอร์ดาวน์โหลดและรัน JavaScript สำหรับ Client Components
4. Hydration Complete
React เชื่อมต่อ event handlers และทำให้เว็บไซต์ interactive
⚠️ Hydration Errors ที่พบบ่อย
สาเหตุ: เนื้อหาที่เรนเดอร์บนเซิร์ฟเวอร์ไม่ตรงกับที่เรนเดอร์บน client
// ❌ ผิด - เวลาต่างกันระหว่าง server และ client function CurrentTime() { return <div>เวลาปัจจุบัน: {new Date().toLocaleTimeString()}</div> } // ✅ ถูก - ใช้ useEffect function CurrentTime() { const [time, setTime] = useState('') useEffect(() => { setTime(new Date().toLocaleTimeString()) }, []) return <div>เวลาปัจจุบัน: {time || 'กำลังโหลด...'}</div> }
// ❌ ผิด - window ไม่มีบน server function WindowWidth() { return <div>หน้าจอกว้าง: {window.innerWidth}px</div> } // ✅ ถูก - ตรวจสอบก่อนใช้ function WindowWidth() { const [width, setWidth] = useState(0) useEffect(() => { setWidth(window.innerWidth) const handleResize = () => setWidth(window.innerWidth) window.addEventListener('resize', handleResize) return () => window.removeEventListener('resize', handleResize) }, []) return <div>หน้าจอกว้าง: {width || 'กำลังตรวจสอบ...'}px</div> }
// ❌ ผิด - Math.random() ให้ค่าต่างกันระหว่าง server และ client function RandomColor() { const color = `#${Math.floor(Math.random() * 16777215).toString(16)}` return <div style={{ color }}>สีสุ่ม</div> } // ✅ ถูก - สร้างค่าสุ่มหลัง hydration function RandomColor() { const [color, setColor] = useState('#000000') useEffect(() => { const randomColor = `#${Math.floor(Math.random() * 16777215).toString(16)}` setColor(randomColor) }, []) return <div style={{ color }}>สีสุ่ม</div> }
💡 Best Practices สำคัญ
ใช้ Server Components เป็น default และเปลี่ยนเป็น Client เมื่อจำเป็น
แยกไฟล์ Server และ Client Components เพื่อความชัดเจน
ใช้ dynamic imports สำหรับ Client Components ที่ไม่จำเป็นต้องโหลดทันที
หลีกเลี่ยงการใช้ browser APIs ใน Server Components
ใช้ Suspense boundaries สำหรับ loading states ที่ดีขึ้น
🎯 ยินดีด้วย! คุณเรียนจบบทที่ 3 แล้ว
ตอนนี้คุณเข้าใจความแตกต่างระหว่าง Server Components และ Client Components แล้ว! พร้อมสำหรับการเรียนรู้เรื่อง Data Fetching และ Caching ในบทถัดไป