Skip to content

Context API — Theme Toggle

Context API เป็นวิธีที่ React มีให้สำหรับ ส่ง data ข้าม component tree โดยไม่ต้องส่ง props ผ่านทุกตัว

flowchart TB
    subgraph "Without Context"
        A[App] --> B[Layout] --> C[Header] --> D[ThemeButton]
    end
    
    subgraph "With Context"
        E[App] -->|"ThemeContext"| F[Any Component]
    end
Avatar whiteCat
ถ้าเปรียบกับการส่งข้อความทางไปรษณีย์ — Context คือกล่องแชร์ที่ทุกคนเข้าถึงได้

src/contexts/ThemeContext.tsx
import { createContext, useContext, useState, useEffect, ReactNode } from "react"
type Theme = "light" | "dark"
interface ThemeContextType {
theme: Theme
toggleTheme: () => void
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>(() => {
// อ่านค่าจาก localStorage หรือ system preference
if (typeof window !== "undefined") {
const saved = localStorage.getItem("theme") as Theme
if (saved) return saved
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
}
return "light"
})
useEffect(() => {
// อัปเดต class ของ HTML element
const root = document.documentElement
if (theme === "dark") {
root.classList.add("dark")
} else {
root.classList.remove("dark")
}
localStorage.setItem("theme", theme)
}, [theme])
const toggleTheme = () => {
setTheme(prev => prev === "light" ? "dark" : "light")
}
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
)
}
export function useTheme() {
const context = useContext(ThemeContext)
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider")
}
return context
}
src/main.tsx
import { ThemeProvider } from "./contexts/ThemeContext"
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ThemeProvider>
<App />
</ThemeProvider>
</React.StrictMode>
)

Step 3: สร้าง Theme Toggle Button

Section titled “Step 3: สร้าง Theme Toggle Button”
src/components/ThemeToggle.tsx
import { useTheme } from "../contexts/ThemeContext"
export function ThemeToggle() {
const { theme, toggleTheme } = useTheme()
return (
<button
onClick={toggleTheme}
className="p-2 rounded-lg bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors"
aria-label={`Switch to ${theme === "light" ? "dark" : "light"} mode`}
>
{theme === "light" ? "🌙" : "☀️"}
</button>
)
}

ใช้งานใน Component ใดก็ได้

Section titled “ใช้งานใน Component ใดก็ได้”
src/components/Header.tsx
import { ThemeToggle } from "./ThemeToggle"
export function Header() {
return (
<header className="flex justify-between items-center p-4 bg-white dark:bg-gray-800">
<h1 className="text-xl font-bold dark:text-white">My Portfolio</h1>
<ThemeToggle />
</header>
)
}
Avatar whiteCat
ตอนนี้ theme จะถูกเก็บใน localStorage ด้วย คนเข้าชมจะเห็น theme ที่เลือกไว้ล่าสุดเสมอ

สิ่งที่ทำคำอธิบาย
createContext()สร้าง context object
useContext()อ่านค่าจาก context
Providerส่งค่าให้ component ลูก
useEffect + localStorageจำ theme ไว้ตลอด
flowchart LR
    ThemeProvider[ThemeProvider<br/>จัดเก็บ state] -->|"theme, toggleTheme"| useTheme[useTheme<br/>Hook]
    useTheme --> ThemeToggle[ThemeToggle<br/>UI Button]
    
    classDef important fill:#a78bfa,stroke:#7c3aed,stroke-width:2px
    class ThemeProvider,useTheme,ThemeToggle important

02. Zustand — เรียนรู้การใช้ Zustand สำหรับ cart store ที่มีประสิทธิภาพมากขึ้น

Quiz: Context API & Theme

ข้อ 1 / 40%

Context API ใช้แก้ปัญหาอะไร?