useContext คือฮุก React ที่ให้คอมโพเนนต์แบบฟังก์ชันสามารถเข้าถึงข้อมูลจากบริบทที่สร้างขึ้นผ่าน createContext ได้โดยตรง บริบทใน React แก้ปัญหา props drilling — การส่ง props ผ่านคอมโพเนนต์ตัวกลางจำนวนมากที่ไม่ได้ใช้ข้อมูลเหล่านี้ด้วยตนเอง ตาม React Documentation (2025) useContext รับออบเจ็กต์บริบทและคืนค่าปัจจุบันที่ตั้งค่าโดย Provider ที่ใกล้ที่สุดด้านบนในแผนผังคอมโพเนนต์ เมื่อค่าใน Provider เปลี่ยนแปลง คอมโพเนนต์ทั้งหมดที่ใช้ useContext จะถูกเรนเดอร์ใหม่โดยอัตโนมัติ
ประเด็นสำคัญ
useContext คือฮุกที่เพิ่มใน React 16.8 พร้อมกับฮุกอื่น ๆ ที่อนุญาตให้อ่านค่าจากบริบท React บริบทเป็นกลไกที่สร้างไว้ใน React ออกแบบมาเพื่อส่งข้อมูลผ่านแผนผังคอมโพเนนต์โดยไม่ต้องส่ง props ด้วยตนเองในแต่ละระดับ useContext แทนที่คอมโพเนนต์ Consumer จาก Context API เดิมและทำให้โค้ดกระชับและอ่านง่ายขึ้น
กรณีการใช้งานทั่วไปของบริบทรวมถึงธีม (สว่าง/มืด) ตำแหน่งที่ตั้งและการแปล (i18n) การยืนยันตัวตนผู้ใช้ การตั้งค่าแอปพลิเคชัน และข้อมูลส่วนกลางอื่น ๆ ที่คอมโพเนนต์จำนวนมากในระดับการซ้อนกันที่แตกต่างกันต้องการ ทีม React แนะนำให้ใช้บริบทสำหรับข้อมูลที่ เป็นส่วนกลาง สำหรับซับทรีของคอมโพเนนต์ แต่ไม่ใช่สำหรับทั้งแอปพลิเคชัน
ตาม React Team — เอกสารบริบท (2025) การใช้บริบทที่ไม่ถูกต้องเป็นหนึ่งในสาเหตุหลักของปัญหาประสิทธิภาพในแอปพลิเคชัน React การเปลี่ยนแปลงค่าใน Provider ทุกครั้งทำให้ผู้บริโภคทั้งหมดเรนเดอร์ใหม่ ไม่ว่าส่วนใดของข้อมูลจะเปลี่ยนแปลง การปรับให้เหมาะสมผ่าน useMemo และการแบ่งบริบทช่วยแก้ปัญหานี้
import { createContext, useContext } from 'react';
// สร้างบริบทด้วยค่าเริ่มต้น
const ThemeContext = createContext('สว่าง');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={`btn-${theme}`}>Click</button>;
}
กลไกบริบทใน React ถูกนำไปใช้ผ่านรูปแบบ Provider-Consumer createContext คืนค่าออบเจ็กต์ที่มีสองเอนทิตี: Provider — คอมโพเนนต์ที่ส่งค่า และออบเจ็กต์บริบทเอง ซึ่งใช้ใน useContext Provider ถูกติดตั้งในแผนผังคอมโพเนนต์และส่งค่าไปยังองค์ประกอบย่อยทั้งหมดโดยไม่คำนึงถึงความลึกของการซ้อนกัน
เมื่อพบการเรียก useContext React จะเดินทางผ่านแผนผังไฟเบอร์เพื่อค้นหา Provider ที่ใกล้ที่สุดสำหรับบริบทนั้น หากพบ Provider ค่าของมันจะถูกคืนค่า หากไม่พบ Provider ค่าเริ่มต้นที่ส่งไปยัง createContext จะถูกคืนค่า การค้นหานี้เกิดขึ้นทุกครั้งที่เรนเดอร์ แต่ต้องขอบคุณ การจดจำโหนดไฟเบอร์ ทำให้รวดเร็วมากและไม่ส่งผลกระทบต่อประสิทธิภาพ
ตาม React — ภายในบริบท (2024) การทำงานภายในของ useContext ใช้รายการเชื่อมโยงของฮุก คล้ายกับ useState แต่ละฮุกเก็บการอ้างอิงไปยังโหนดไฟเบอร์ ทำให้ React สามารถระบุได้อย่างรวดเร็วว่า Provider ใดสอดคล้องกับบริบทนั้น เมื่อ Provider อัปเดตค่า React จะทำเครื่องหมายโหนดไฟเบอร์ทั้งหมดที่ใช้บริบทนั้นสำหรับการเรนเดอร์ใหม่
คอมโพเนนต์ Provider สามารถซ้อนกันภายในกันและกัน สร้างลำดับชั้นของบริบท แต่ละ Provider ย่อยแทนที่ค่าแม่สำหรับซับทรีของตนเอง สิ่งนี้มีประโยชน์เมื่อหน้าจอหนึ่งต้องการธีมสว่างในขณะที่หน้าต่างโมดอลที่ซ้อนกันต้องการธีมมืด useContext จะคืนค่าของ Provider ที่ใกล้ที่สุดด้านบนในแผนผังเสมอ
const UserContext = createContext(null);
const ThemeContext = createContext('สว่าง');
function App() {
return (
<UserContext.Provider value={{ name: 'Alice' }}>
<ThemeContext.Provider value='dark'>
<Profile />
</ThemeContext.Provider>
</UserContext.Provider>
);
}
ฟังก์ชัน createContext(defaultValue) สร้างออบเจ็กต์บริบท พารามิเตอร์ defaultValue ใช้เมื่อคอมโพเนนต์เรียก useContext แต่ไม่มี Provider ที่สอดคล้องกันด้านบนในแผนผัง หากไม่มี defaultValue useContext จะคืนค่า undefined ซึ่งอาจนำไปสู่ข้อผิดพลาดที่ไม่คาดคิด ขอแนะนำให้ส่งค่าเริ่มต้นที่มีความหมายหรือ null เสมอ
การสร้างโพรไวเดอร์แบบกำหนดเองเป็นรูปแบบทั่วไปสำหรับการห่อหุ้มตรรกะบริบท ภายในโพรไวเดอร์ดังกล่าว สถานะจะถูกเก็บ (ผ่าน useState หรือ useReducer) และส่งผ่าน prop value ของ Provider ซึ่งซ่อนรายละเอียดการทำงานจากคอมโพเนนต์ผู้บริโภคและรวมศูนย์ตรรกะการจัดการบริบทไว้ในที่เดียว
// โพรไวเดอร์แบบกำหนดเองพร้อมการจัดการสถานะ
const AuthContext = createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = useCallback(async (email, pass) => {
const u = await loginApi(email, pass);
setUser(u);
}, []);
return (
<AuthContext.Provider value={{ user, login }}>
{children}
</AuthContext.Provider>
);
}
ในคอมโพเนนต์แบบฟังก์ชัน useContext เป็นวิธีเดียวในการเข้าถึงบริบท มันแทนที่คอมโพเนนต์ Consumer จาก Context API เดิม ซึ่งต้องใช้รูปแบบ render-prop: <ThemeContext.Consumer>{value => ...}</ThemeContext.Consumer> useContext ทำให้โค้ดเป็นเชิงเส้นและอ่านง่ายขึ้น โดยเฉพาะเมื่อทำงานกับหลายบริบทในคอมโพเนนต์เดียว
เมื่อใช้หลายบริบทในคอมโพเนนต์เดียว เพียงเรียก useContext หลายครั้งสำหรับแต่ละบริบท แต่ละการเรียกคืนค่าของ Provider ที่สอดคล้องกัน ลำดับการเรียกไม่สำคัญ เนื่องจากแต่ละบริบทเป็นเอนทิตีอิสระ React ปรับการเรียกหลายครั้งให้เหมาะสมผ่านระบบการอ้างอิงไฟเบอร์เดียวกัน
function Dashboard() {
const { user } = useContext(AuthContext);
const theme = useContext(ThemeContext);
const { locale } = useContext(I18nContext);
return (
<div className={`dashboard-${theme}`}>
<h1>{locale.greeting}, {user.name}</h1>
</div>
);
}
การเลือกระหว่าง useContext และ Redux ขึ้นอยู่กับขนาดและความซับซ้อนของการจัดการสถานะ useContext + useReducer เป็นตัวแทนน้ำหนักเบาของ Redux สำหรับแอปพลิเคชันขนาดเล็กและขนาดกลาง ไม่ต้องติดตั้งไลบรารีภายนอก เรียนรู้ง่าย และเพียงพอสำหรับงานส่วนใหญ่ Redux เหมาะสมเมื่อต้องการสถาปัตยกรรมที่เข้มงวดกับ middleware เครื่องมือสำหรับนักพัฒนา และการอัปเดตที่ไม่เปลี่ยนสภาพ
ข้อได้เปรียบหลักของ Redux เหนือ useContext คือการปรับการเรนเดอร์ใหม่ให้เหมาะสม โดยค่าเริ่มต้น เมื่อค่าใน Provider เปลี่ยนแปลง ผู้บริโภคบริบททั้งหมดจะเรนเดอร์ใหม่ Redux กับ useSelector และ shallowEqual อนุญาตให้คอมโพเนนต์สมัครรับเฉพาะส่วนเฉพาะของสถานะ ซึ่ง ลดจำนวนการเรนเดอร์ใหม่ในแอปพลิเคชันขนาดใหญ่ได้อย่างมาก บริทยังสามารถปรับให้เหมาะสมโดยการแบ่งเป็นบริบทเล็ก ๆ หลายอัน
| เกณฑ์ | useContext | Redux |
|---|---|---|
| ความซับซ้อน | ไม่มีการพึ่งพาภายนอก | ต้องตั้งค่า store และ middleware |
| การเรนเดอร์ใหม่ | ผู้บริโภคทั้งหมดเมื่อมีการเปลี่ยนแปลง | เฉพาะที่สมัครรับ slice เฉพาะ |
| DevTools | React DevTools | Redux DevTools ที่ย้อนเวลาได้ |
| Middleware | ไม่รองรับ | Redux Thunk, Saga, Observable |
| เมื่อใดควรเลือก | แอปขนาดกลาง 3–5 บริบท | แอปขนาดใหญ่ที่มีตรรกะทางธุรกิจซับซ้อน |
ตาม ผู้ดูแล Redux — เมื่อใดควรใช้ Redux (2024) 70% ของแอปพลิเคชัน React ไม่จำเป็นต้องใช้ Redux หากคุณมีคอมโพเนนต์น้อยกว่า 50 ตัวและสถานะไม่มีตรรกะซับซ้อนกับการแคช debounce และเอฟเฟกต์ข้างเคียง — useContext + useReducer ก็เพียงพอแล้ว Redux เพิ่มโค้ดเทมเพลตและควรใช้อย่างมีสติ
ข้อผิดพลาดที่พบบ่อยที่สุดคือ การสร้างออบเจ็กต์ value ใหม่ ทุกครั้งที่ Provider เรนเดอร์ หากคุณส่ง value={{ user, login }} ให้ Provider ออบเจ็กต์ใหม่จะถูกสร้างขึ้นทุกครั้งที่ Provider เรนเดอร์ ทำให้ผู้บริโภคทั้งหมดเรนเดอร์ใหม่แม้ว่าข้อมูลจะไม่เปลี่ยนแปลง วิธีแก้คือจดจำค่าด้วย useMemo หรือใช้บริบทแยกสำหรับข้อมูลที่เปลี่ยนแปลงบ่อยและนานครั้ง
// ❌ ออบเจ็กต์ใหม่ทุกครั้งที่เรนเดอร์ — ผู้บริโภคทั้งหมดเรนเดอร์ใหม่
<AuthContext.Provider value={{ user, login }}>{children}</AuthContext.Provider>
// ✅ ค่าที่จดจำไว้ — เรนเดอร์ใหม่เมื่อ user หรือ login เปลี่ยนแปลงเท่านั้น
const authValue = useMemo(() => ({ user, login }), [user, login]);
<AuthContext.Provider value={authValue}>{children}</AuthContext.Provider>
เพื่อแก้ปัญหา “บริบทใหญ่” ให้แบ่งสถานะส่วนกลางเป็นกลุ่มตรรกะ: AuthContext, ThemeContext, I18nContext แต่ละบริบทรัรันผิดชอบพื้นที่ของตนเองและอัปเดตอย่างอิสระ วิธีนี้ง่ายกว่าการพยายามปรับบริบทยักษ์เดียวให้เหมาะสมผ่าน useMemo และให้พฤติกรรมการเรนเดอร์ใหม่ที่คาดเดาได้มากขึ้น
คำถามที่พบบ่อย
ได้ หากคุณส่งฟังก์ชันการเปลี่ยนแปลงใน value ของ Provider รูปแบบทั่วไปคือเก็บสถานะใน Provider และส่งทั้งข้อมูลและฟังก์ชันอัปเดตผ่าน useContext คอมโพเนนต์ย่อยเรียกฟังก์ชันเหล่านี้ และการเปลี่ยนสถานะใน Provider จะอัปเดตผู้บริโภคทั้งหมดโดยอัตโนมัติ นี่เป็นตัวแทนพื้นฐานของ Redux สำหรับแอปพลิเคชันขนาดเล็ก
สำหรับสถานการณ์ง่าย ๆ useContext เร็วกว่าเนื่องจากไม่มีโอเวอร์เฮดจาก store และ middleware แต่ด้วยการอัปเดตบ่อยครั้งและผู้บริโภคจำนวนมาก Redux ชนะเพราะตัวเลือก (useSelector) ของมันสมัครรับเฉพาะส่วนเฉพาะของสถานะ ในขณะที่ useContext เรนเดอร์ผู้บริโภคทั้งหมดใหม่เมื่อมีการเปลี่ยนแปลงใด ๆ สำหรับแอปพลิเคชันที่มีความถี่ในการอัปเดตสูง (แอนิเมชัน, เรียลไทม์) ให้เลือก Redux หรือไลบรารีเฉพาะทาง
ไม่ได้ useContext เช่นเดียวกับฮุกทั้งหมด สามารถเรียกได้ภายในคอมโพเนนต์แบบฟังก์ชัน React หรือฮุกที่กำหนดเองเท่านั้น หากคุณต้องการรับค่าบริบทในฟังก์ชันปกติ (เช่น ในยูทิลิตีหรือบริการ) ให้ส่งเป็นพารามิเตอร์จากคอมโพเนนต์หรือใช้โมดูลแยกต่างหากกับสถานะส่วนกลางภายนอก React
การกำหนดชนิดบริบทใน TypeScript ทำได้โดยระบุชนิดใน createContext: createContext<AuthContextType | null>(null) สิ่งนี้รับประกันว่า useContext(AuthContext) คืนค่าชนิดที่ถูกต้อง รูปแบบที่สะดวกคือการสร้างฮุกแบบกำหนดเอง useAuth ที่เรียก useContext ตรวจสอบ null และแสดงข้อผิดพลาดที่ชัดเจน: “ต้องใช้ useAuth ภายใน AuthProvider”
สาเหตุที่พบบ่อยที่สุดคือคอมโพเนนต์ผู้บริโภค ไม่ได้อยู่ภายใน Provider ที่สอดคล้องกัน ตรวจสอบว่า Provider หุ้มซับทรีทั้งหมดที่ใช้ useContext อยู่ สาเหตุที่สอง — มีการส่งออบเจ็กต์บริบทอื่นให้ Provider: นักพัฒนาสร้างบริบทด้วยการเรียก createContext แต่ใช้ useContext กับอินสแตนซ์อื่นของ createContext
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม