Context
Context: teleport ค่าลงไปทั้ง subtree
หัวข้อที่มีชื่อว่า “Context: teleport ค่าลงไปทั้ง subtree”Context ให้ parent ทำให้ค่าหนึ่งพร้อมใช้กับทุก component ที่อยู่ข้างล่าง โดยไม่ต้องส่ง props ผ่านชั้นตรงกลาง คุณสร้าง context, ห่อ subtree ด้วย provider แล้ว descendant ตัวไหนก็อ่านค่านั้นได้ด้วย useContext
import { createContext, useContext, useState } from "react";
const ThemeContext = createContext("light");
function App() { const [theme, setTheme] = useState("dark"); return ( // React 19: you can render <ThemeContext> directly as the provider. // (The classic <ThemeContext.Provider> still works too.) <ThemeContext value={theme}> <Page /> </ThemeContext> );}
function Button() { const theme = useContext(ThemeContext); // reads the nearest provider's value return <button className={theme}>Click</button>;}Button ได้ theme ไม่ว่าจะอยู่ลึกแค่ไหน และไม่มี component ตรงกลางต้องส่งต่อค่านั้น นี่คือทางแก้ prop drilling เมื่อ composition เอื้อมไปถึง consumer ไม่ได้
เมื่อไร Context เหมาะ — และเมื่อไรกลับเจ็บ
หัวข้อที่มีชื่อว่า “เมื่อไร Context เหมาะ — และเมื่อไรกลับเจ็บ”Context ถูกออกแบบมาสำหรับค่าที่ แชร์กว้างและเปลี่ยนไม่บ่อย: theme, user/auth ปัจจุบัน, locale, config ของ design-system สิ่งที่หลาย component อ่านแต่เปลี่ยนน้อย
จุดที่ต้องระวังคือ performance: เมื่อ value ของ provider เปลี่ยน ทุก consumer จะ re-render — และ React หยุดการ re-render นั้นด้วย memo ไม่ได้ เพราะ consumer subscribe context โดยตรง
flowchart TB prov["value ของ Provider เปลี่ยน"] --> c1["Consumer A re-render"] prov --> c2["Consumer B re-render"] prov --> c3["Consumer C re-render"] note["memo หยุด context re-render ไม่ได้"] -.-> c1
ดังนั้นการเอา state ที่ เปลี่ยนเร็ว (เช่นค่าที่ update ทุก keystroke หรือทุก animation frame) ไปใส่ context กว้าง ๆ ตัวเดียวคือกับดัก performance — จะ re-render ทั้ง subtree ตลอดเวลา
มีสองวิธีบรรเทา:
- Split context. แยกค่าที่ไม่เกี่ยวกันไว้คนละ provider เพื่อให้การเปลี่ยนตัวหนึ่งไม่ re-render consumer ของอีกตัว การ split ที่พบบ่อย: context หนึ่งสำหรับ state และอีกหนึ่งสำหรับ dispatch/updater (updater stable อยู่แล้ว ดังนั้น component ที่ dispatch อย่างเดียวจะไม่ re-render)
- ทำให้ value stable. memoize object
valueของ provider เพื่อไม่ให้ได้ reference ใหม่ทุกครั้งที่ parent render
Context ไม่ใช่ state manager
หัวข้อที่มีชื่อว่า “Context ไม่ใช่ state manager”ความแตกต่างสำคัญ: Context เป็นกลไก ขนส่ง ไม่ใช่ที่เก็บ state Context ย้ายค่าลงไปตาม tree ไม่ได้ถือหรือ update state ตัว state เองยังอยู่ใน useState หรือ useReducer ใน component ที่เป็น provider “ใช้ Context สำหรับ state management” จริง ๆ แล้วหมายถึง “useState/useReducer ข้างบน แล้วส่งด้วย Context”
function AuthProvider({ children }) { const [user, setUser] = useState(null); // the state lives here return <AuthContext value={{ user, setUser }}>{children}</AuthContext>; // Context just delivers it}