Dark mode
React
Getting Started
React หรือ Next.js ดี?
หากเริ่มต้นโปรเจคใหม่ Next.js อาจเป็นตัวเลือกที่ดีกว่า เพราะมาพร้อมระบบ routing, Server-Side Rendering (SSR), และ Static Site Generation (SSG) ในตัว ช่วยให้การพัฒนาเว็บแอปพลิเคชันที่มีประสิทธิภาพและ SEO-friendly ทำได้ง่ายขึ้น แม้เพิ่งเริ่มต้นกับ
React ก็สามารถเรียนรู้ผ่าน Next.js ได้เลย
คุณสมบัติ | React | Next.js |
---|---|---|
ประเภท | ไลบรารี UI | เฟรมเวิร์กที่สร้างบน React 🚀 |
สถาปัตยกรรม | Client-Side Rendering (CSR) | CSR, SSR, SSG, ISR 🚀 |
Routing | ต้องใช้ไลบรารีเสริม (เช่น React Router) | App Router (File-based) ✅ |
โครงสร้างโปรเจค | ยืดหยุ่นสูง ⚖️ | กำหนดโครงสร้างมาตรฐาน |
SEO | ต้องตั้งค่าและจัดการเอง | เหมาะสมกับ SEO อย่างยิ่ง ✅ |
Server-Side Rendering | ต้องตั้งค่าเอง | พร้อมใช้งาน ✅ |
Static Site Generation | ต้องใช้เครื่องมือเสริม (เช่น Gatsby) | พร้อมใช้งาน ✅ |
การตั้งค่า | ยืดหยุ่น แต่ต้องตั้งค่าเองเยอะ | มีการตั้งค่าเริ่มต้นที่ดีที่สุด ✅ |
เหมาะสำหรับ | แอปขนาดเล็ก-กลาง, SPA ✅ | แอปขนาดใหญ่, เว็บไซต์, E-commerce ✅ |
ชุมชน | ใหญ่มาก ✅ | ใหญ่และเติบโตเร็ว |
เอกสาร | ละเอียดและครอบคลุม ✅ | ยอดเยี่ยมและทันสมัย |
การพัฒนาเร็ว | เร็ว (เมื่อตั้งค่าเสร็จ) ✅ | เร็วมาก มีเครื่องมือครบครัน |
ความปลอดภัย | ขึ้นอยู่กับการ implement | มีฟีเจอร์ความปลอดภัยในตัว ✅ |
การทดสอบ | ต้องตั้งค่าเอง | รองรับการทดสอบในตัว (Jest, Playwright) ✅ |
Basic Concepts
Components (for UI)
ส่วนประกอบพื้นฐานของ React ที่ใช้สร้าง UI โดยแบ่งเป็นไฟล์ .jsx หรือ .tsx แยกกัน
ส่วนประกอบของ Components:
ประเภทของ State | คำอธิบาย | ตัวอย่าง | กรณีการใช้งาน |
---|---|---|---|
Props | ค่าที่รับเข้ามาในคอมโพเนนต์ | interface Props { msg: string } | ส่งข้อมูลจาก Parent ไปยัง Child component |
Callback Functions | ฟังก์ชันที่ส่งเป็น props เพื่อให้ Child เรียกกลับ | (value: string) => void | แจ้ง Parent เมื่อมีการเปลี่ยนแปลงใน Child |
children Prop | ช่องสำหรับส่งเนื้อหา JSX/Component | <Parent> <Child /> </Parent> | สร้าง Component ที่มีโครงสร้างยืดหยุ่น |
Context API | ส่งค่าข้ามหลายระดับคอมโพเนนต์ | createContext() / useContext() | ส่งข้อมูลระหว่าง Component ที่ซ้อนกันหลายชั้น |
1. Props - ส่งข้อมูลจาก Parent ไป Child
tsx
// ตัวอย่างการใช้ Props
import ChildComponent from './ChildComponent';
function ParentComponent() {
// ข้อมูลที่จะส่งเป็น props
const message = 'สวัสดีจาก Parent';
return (
// ส่ง props ชื่อ msg ไปยัง ChildComponent
<ChildComponent msg={message} />
);
}
tsx
// กำหนดประเภทของ props ที่จะรับ
interface Props {
msg: string;
}
// รับและแสดง props ที่ชื่อ msg
function ChildComponent({ msg }: Props) {
return <div>{msg}</div>;
}
2. Callback Functions - ส่งเหตุการณ์จาก Child ไป Parent
tsx
// ตัวอย่างการใช้ Callback
interface Props {
onUpdate: (value: string) => void;
}
function ChildComponent({ onUpdate }: Props) {
const newValue = 'ข้อมูลที่อัปเดตแล้ว';
return (
// เมื่อคลิกปุ่มจะเรียกฟังก์ชัน onUpdate ที่รับมาจาก props
<button onClick={() => onUpdate(newValue)}>อัปเดต</button>
);
}
tsx
import ChildComponent from './ChildComponent';
function ParentComponent() {
// ฟังก์ชันที่ทำงานเมื่อได้รับ event 'update'
const handleUpdate = (value: string) => {
console.log('ได้รับค่า:', value);
};
return (
// ส่งฟังก์ชัน handleUpdate เป็น prop 'onUpdate' ไปยัง Child
<ChildComponent onUpdate={handleUpdate} />
);
}
3. children
Prop - ส่งเนื้อหา JSX/Component
tsx
import React from 'react';
interface Props {
children: React.ReactNode;
}
function BaseLayout({ children }: Props) {
return (
<div className="container">
<header>
{/* สามารถแยก header เป็น prop เฉพาะได้ หรือใช้ children */}
</header>
<main>
{children} {/* Default slot equivalent */}
</main>
</div>
);
}
tsx
import BaseLayout from './BaseLayout';
function ParentComponent() {
return (
<BaseLayout>
<h1>Page Title</h1>
<p>Main content goes here</p>
</BaseLayout>
);
}
4. Context API - ส่งค่าข้ามหลายระดับ Component
tsx
import { createContext } from 'react';
export const ThemeContext = createContext('light'); // Default value 'light'
tsx
import { ThemeContext } from './theme-context';
import ChildComponent from './ChildComponent';
function RootComponent() {
return (
<ThemeContext.Provider value="dark">
<ChildComponent />
</ThemeContext.Provider>
);
}
tsx
import { useContext } from 'react';
import { ThemeContext } from './theme-context';
function DeeplyNestedChild() {
const theme = useContext(ThemeContext);
return <div className={theme}>...</div>;
}
Events (for User Interactions)
รูปแบบ | React | คำอธิบาย | กรณีการใช้งาน |
---|---|---|---|
Native Events | onClick , onChange | Event ปกติของ DOM | การจัดการคลิกปุ่ม, การเปลี่ยนแปลง input |
Custom Events | ใช้ callback props | การส่ง event จาก child ไป parent | เมื่อต้องการให้ child component แจ้งเหตุการณ์ไปยัง parent |
Event Modifiers | ต้องเขียน logic เอง | การปรับเปลี่ยนพฤติกรรม event | เมื่อต้องการป้องกันการ submit form, หยุดการ bubbling ของ event |
tsx
function Button() {
const handleClick = () => console.log('Clicked!');
return (
<>
<button onClick={handleClick}>Click Me</button>
<input onChange={(e) => console.log(e.target.value)} />
</>
);
}
tsx
function Child({ onAction }: { onAction: (value: string) => void }) {
return (
<button onClick={() => onAction('Hello')}>
Trigger Action
</button>
);
}
function Parent() {
const handleAction = (value: string) => {
console.log(`Action with value: ${value}`);
};
return <Child onAction={handleAction} />;
}
tsx
function Form() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); // แทน .prevent modifier
e.stopPropagation(); // แทน .stop modifier
console.log('Form submitted');
};
return <form onSubmit={handleSubmit}>...</form>;
}
State (for Component Data)
Hook | คำอธิบาย | กรณีการใช้งาน |
---|---|---|
useState | สำหรับ state ง่ายๆ | การนับ, สถานะ toggle, ข้อมูลแบบง่าย |
useReducer | สำหรับ state ที่ซับซ้อน | Form state, state machine |
useContext | สำหรับ global state | Theme, การตั้งค่าทั่วไป |
tsx
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</div>
);
}
tsx
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
tsx
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return (
<div style={{
background: theme === 'dark' ? '#333' : '#FFF',
color: theme === 'dark' ? '#FFF' : '#333'
}}>
Current theme: {theme}
</div>
);
}
Hooks (for Reusing Logic)
React มี built-in hooks ให้ใช้งานหลายตัว และยังสามารถสร้าง custom hooks ของตัวเองได้
Built-in Hooks
Hook | คำอธิบาย | กรณีการใช้งาน | หมวดหมู่ |
---|---|---|---|
useState | บันทึกและอัปเดต state ใน functional component | การจัดการ state ระดับ component | State Management |
useEffect | ดำเนินการ side effects ใน functional component | การ fetch ข้อมูล, การติดตั้ง event listeners | Side Effects |
useMemo | Memoize ค่า | การคำนวณที่ซับซ้อน | Performance |
useCallback | Memoize function | การส่งฟังก์ชันเป็น props | Performance |
useRef | เก็บค่าที่ไม่ทำให้ re-render | อ้างอิง DOM, เก็บค่า previous | DOM Access |
useContext | เข้าถึง context value จาก provider | Theme, การตั้งค่าทั่วไป | Context |
useReducer | จัดการ state ที่ซับซ้อน | Form state, state machine | State Management |
useTransition | จัดการการอัปเดตที่ไม่ได้เร่งด่วน | การโหลดข้อมูลแบบไม่บล็อก UI | Performance |
useDeferredValue | เลื่อนการอัปเดต UI | การค้นหาที่มีการพิมพ์บ่อยๆ | Performance |
useId | สร้าง ID ที่ไม่ซ้ำกัน | Form labels, ARIA attributes | DOM Access |
useSyncExternalStore | การใช้งานกับ external stores | การเชื่อมต่อกับ Redux, Zustand | State Management |
useInsertionEffect | สำหรับ CSS-in-JSS libraries | การ inject style ที่ runtime | DOM Access |
Custom Hooks
เราสามารถสร้าง hooks ของตัวเองได้โดยการรวม built-in hooks เข้าด้วยกัน
tsx
export function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(c => c + 1);
const decrement = () => setCount(c => c - 1);
return { count, increment, decrement };
}
tsx
function Counter() {
const { count, increment } = useCounter();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
Lifecycle / Effects (with Hooks)
วงจรชีวิตของคอมโพเนนต์และการจัดการ side effects ด้วย useEffect
Hook | คำอธิบาย | กรณีการใช้งาน |
---|---|---|
useEffect(() => { return () => {} }, []) | ลบ event listeners, ยกเลิก timers/subscriptions | การยกเลิกการอัปเดตเมื่อ component ถูกถอดออก |
useEffect(() => {}, [dep]) | ทำงานเมื่อ dependency dep เปลี่ยนแปลง | การอัปเดตเมื่อข้อมูลเปลี่ยนแปลง |
useLayoutEffect | ทำงานกับ DOM ทันทีหลัง render แต่ก่อน paint | การอัปเดต DOM หลัง render |
tsx
useEffect(() => {
console.log('Component mounted');
return () => {
console.log('Component unmounted');
};
}, []);
tsx
useEffect(() => {
console.log('Value changed:', value);
}, [value]);
tsx
useLayoutEffect(() => {
const rect = elementRef.current?.getBoundingClientRect();
console.log('Element size:', rect);
});
Styling (for CSS)
การจัดการสไตล์และ CSS ใน React Components
วิธีการ | คำอธิบาย | เมื่อไหร่ที่ควรใช้ |
---|---|---|
CSS Modules | Import ไฟล์ .module.css สไตล์จะมีผลเฉพาะที่ | วิธีมาตรฐานสำหรับสไตล์เฉพาะคอมโพเนนต์ |
Utility Frameworks | Tailwind/UnoCSS คลาส utility แบบ atomic | การสร้างเร็ว, โปรเจคขนาดเล็ก-กลาง |
CSS-in-JS | Styled Components, Emotion เขียน CSS ใน JS | การสร้างสไตล์แบบไดนามิกที่ซับซ้อน |
tsx
function MyButton() {
return (
<div className="p-4 text-red-500 hover:text-blue-500 transition">
<button className="px-4 py-2 bg-blue-500 rounded text-white">
Click Me
</button>
</div>
);
}
tsx
import clsx from 'clsx';
import { useState } from 'react';
function DynamicComponent() {
const [isError, setIsError] = useState(true);
const [isImportant, setIsImportant] = useState(false);
return (
<div
className={clsx(
'base-class',
isError && 'text-red-500',
isImportant && 'font-bold'
)}
>
Dynamic Content
</div>
);
}
JSX (for Rendering)
ไวยากรณ์พื้นฐานใน JSX ของ React สำหรับการเรนเดอร์
Control Flow
แนวคิด | ตัวอย่าง | กรณีการใช้งาน |
---|---|---|
Conditional Rendering | {show && <div>...</div>} | การแสดงผลแบบมีเงื่อนไข |
List Rendering | items.map(item => <li>...</li>) | การแสดงรายการข้อมูล |
Dynamic Attributes | className={isActive ? 'active' : ''} | การกำหนด attribute แบบไดนามิก |
Event Handling | onClick={handleClick} | การจัดการเหตุการณ์ |
tsx
function Conditional({ showMessage, isVisible }) {
return (
<div>
{showMessage && <p>This appears when showMessage is true</p>}
{isVisible ? <div>Visible</div> : <div>Hidden</div>}
</div>
);
}
tsx
function List({ items }) {
return (
<ul>
{items.map((item, index) => (
<li key={item.id}>
{index + 1}. {item.name}
</li>
))}
</ul>
);
}
tsx
import clsx from 'clsx';
function DynamicStyles({ isActive, hasError, size }) {
return (
<div
className={clsx({ active: isActive, 'text-red': hasError })}
style={{ fontSize: `${size}px` }}
>
Dynamic Styles
</div>
);
}
tsx
function InteractiveButton() {
const handleClick = () => console.log('Clicked');
const onHover = () => console.log('Hovered');
const handleSubmit = (e) => e.preventDefault();
return (
<button
onClick={handleClick}
onMouseOver={onHover}
onSubmit={handleSubmit}
>
Interactive Button
</button>
);
}
Components
แนวคิด | ตัวอย่าง | กรณีการใช้งาน |
---|---|---|
Composition (children ) | <Card>...</Card> | การประกอบคอมโพเนนต์ |
Conditional Rendering | {currentView === 'A' ? <CompA /> : <CompB />} | การสลับคอมโพเนนต์ |
Animation Libraries | <FramerComponent> | เอฟเฟกต์การเปลี่ยนภาพ |
tsx
// Parent
function App() {
return (
<Card title="Custom Header">
<p>Default Content</p>
</Card>
);
}
// Child (Card.tsx)
function Card({ title, children }) {
return (
<div>
<h1>{title}</h1>
{children}
</div>
);
}
tsx
import { useState } from 'react';
import Home from './Home';
import Profile from './Profile';
function App() {
const [currentView, setCurrentView] = useState('Home');
return currentView === 'Home' ? <Home /> : <Profile />;
}
tsx
import { motion } from 'framer-motion';
function FadingComponent({ isVisible }) {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: isVisible ? 1 : 0 }}
transition={{ duration: 0.5 }}
>
I fade in and out
</motion.div>
);
}
APIs
React Core API
API หลักของ React สำหรับการสร้างและจัดการ component
API | คำอธิบาย | กรณีการใช้งาน | หมวดหมู่ |
---|---|---|---|
useState() | บันทึกและอัปเดต state ใน functional component | การจัดการ state ระดับ component | State Management |
useEffect() | ดำเนินการ side effects ใน functional component | การ fetch ข้อมูล, การติดตั้ง event listeners | Side Effects |
useContext() | เข้าถึง context value จาก provider | การแชร์ข้อมูลระหว่างหลาย component | Context |
useReducer() | จัดการ state ที่ซับซ้อน | การจัดการ state ที่มี logic ซับซ้อน | State Management |
useMemo() | Memoize ค่าเพื่อป้องกันการคำนวณซ้ำ | เพิ่มประสิทธิภาพเมื่อมีค่าที่คำนวณหนัก | Performance |
useCallback() | Memoize function เพื่อป้องกันการสร้างใหม่ | เพิ่มประสิทธิภาพเมื่อส่ง callback ไปยัง child components | Performance |
memo() | ป้องกันการ re-render เมื่อ props ไม่เปลี่ยนแปลง | เพิ่มประสิทธิภาพสำหรับ component ที่ render บ่อย | Performance |
React DOM
API สำหรับการทำงานกับ DOM ในแอปพลิเคชัน React
API | คำอธิบาย | กรณีการใช้งาน | หมวดหมู่ |
---|---|---|---|
render() | แสดงผล React element เข้าไปใน DOM | การแสดงผลแอป React ในเบราว์เซอร์ | Rendering |
hydrate() | เหมือน render() แต่สำหรับเนื้อหาที่แสดงผลจากฝั่งเซิร์ฟเวอร์ | การทำให้หน้า SSR ทำงานแบบไดนามิก | SSR |
createPortal() | แสดงผล children ใน DOM node นอกลำดับชั้น parent | Modal, Tooltip, หรือ popup ที่ต้องแสดงเหนือองค์ประกอบอื่น | Advanced DOM |
unmountComponentAtNode() | ลบ React component ที่ถูก mount ออกจาก DOM | การทำ cleanup เมื่อปิดแอปหรือเปลี่ยนหน้า | Lifecycle |
findDOMNode() | หา DOM node ของ class component instance | ใช้เมื่อต้องการเข้าถึง DOM โดยตรง (ไม่แนะนำให้ใช้บ่อย) | DOM Access |
createRoot() | สร้าง root สำหรับ concurrent rendering (React 18+) | การตั้งค่าแอป React 18 แบบใหม่ | Concurrent Mode |
hydrateRoot() | สร้าง root สำหรับ hydrating เนื้อหาจากเซิร์ฟเวอร์ (React 18+) | การ hydrate แอป React 18 ที่แสดงผลจากเซิร์ฟเวอร์ | Concurrent Mode |