useContext: แก่นแท้ ฮุกการเข้าถึงบริบท และโพรไวเดอร์ใน React

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-07-04 เวลาอ่าน: 9 นาที

useContext คือฮุก React ที่ให้คอมโพเนนต์แบบฟังก์ชันสามารถเข้าถึงข้อมูลจากบริบทที่สร้างขึ้นผ่าน createContext ได้โดยตรง บริบทใน React แก้ปัญหา props drilling — การส่ง props ผ่านคอมโพเนนต์ตัวกลางจำนวนมากที่ไม่ได้ใช้ข้อมูลเหล่านี้ด้วยตนเอง ตาม React Documentation (2025) useContext รับออบเจ็กต์บริบทและคืนค่าปัจจุบันที่ตั้งค่าโดย Provider ที่ใกล้ที่สุดด้านบนในแผนผังคอมโพเนนต์ เมื่อค่าใน Provider เปลี่ยนแปลง คอมโพเนนต์ทั้งหมดที่ใช้ useContext จะถูกเรนเดอร์ใหม่โดยอัตโนมัติ

ประเด็นสำคัญ

  • useContext — ฮุกสำหรับอ่านค่าจากบริบท React โดยไม่ต้อง props drilling
  • createContext — สร้างออบเจ็กต์บริบทด้วยค่าเริ่มต้นและคอมโพเนนต์ Provider
  • Provider — คอมโพเนนต์หุ้มที่ส่งค่าบริบทไปยังองค์ประกอบย่อยทั้งหมด
  • การเรนเดอร์ใหม่ — การเปลี่ยนค่า Provider ทำให้ผู้บริโภคบริบททั้งหมดเรนเดอร์ใหม่
  • การข้ามคอมโพเนนต์ตัวกลาง — useContext อนุญาตให้ส่งข้อมูลผ่านหลายระดับของการซ้อนกัน

useContext ใน React คืออะไร

useContext คือฮุกที่เพิ่มใน React 16.8 พร้อมกับฮุกอื่น ๆ ที่อนุญาตให้อ่านค่าจากบริบท React บริบทเป็นกลไกที่สร้างไว้ใน React ออกแบบมาเพื่อส่งข้อมูลผ่านแผนผังคอมโพเนนต์โดยไม่ต้องส่ง props ด้วยตนเองในแต่ละระดับ useContext แทนที่คอมโพเนนต์ Consumer จาก Context API เดิมและทำให้โค้ดกระชับและอ่านง่ายขึ้น

กรณีการใช้งานทั่วไปของบริบทรวมถึงธีม (สว่าง/มืด) ตำแหน่งที่ตั้งและการแปล (i18n) การยืนยันตัวตนผู้ใช้ การตั้งค่าแอปพลิเคชัน และข้อมูลส่วนกลางอื่น ๆ ที่คอมโพเนนต์จำนวนมากในระดับการซ้อนกันที่แตกต่างกันต้องการ ทีม React แนะนำให้ใช้บริบทสำหรับข้อมูลที่ เป็นส่วนกลาง สำหรับซับทรีของคอมโพเนนต์ แต่ไม่ใช่สำหรับทั้งแอปพลิเคชัน

ตาม React Team — เอกสารบริบท (2025) การใช้บริบทที่ไม่ถูกต้องเป็นหนึ่งในสาเหตุหลักของปัญหาประสิทธิภาพในแอปพลิเคชัน React การเปลี่ยนแปลงค่าใน Provider ทุกครั้งทำให้ผู้บริโภคทั้งหมดเรนเดอร์ใหม่ ไม่ว่าส่วนใดของข้อมูลจะเปลี่ยนแปลง การปรับให้เหมาะสมผ่าน useMemo และการแบ่งบริบทช่วยแก้ปัญหานี้

jsx
import { createContext, useContext } from 'react';

// สร้างบริบทด้วยค่าเริ่มต้น
const ThemeContext = createContext('สว่าง');

function ThemedButton() {
    const theme = useContext(ThemeContext);
    return <button className={`btn-${theme}`}>Click</button>;
}

บริบทใน React ทำงานอย่างไร

กลไกบริบทใน React ถูกนำไปใช้ผ่านรูปแบบ Provider-Consumer createContext คืนค่าออบเจ็กต์ที่มีสองเอนทิตี: Provider — คอมโพเนนต์ที่ส่งค่า และออบเจ็กต์บริบทเอง ซึ่งใช้ใน useContext Provider ถูกติดตั้งในแผนผังคอมโพเนนต์และส่งค่าไปยังองค์ประกอบย่อยทั้งหมดโดยไม่คำนึงถึงความลึกของการซ้อนกัน

เมื่อพบการเรียก useContext React จะเดินทางผ่านแผนผังไฟเบอร์เพื่อค้นหา Provider ที่ใกล้ที่สุดสำหรับบริบทนั้น หากพบ Provider ค่าของมันจะถูกคืนค่า หากไม่พบ Provider ค่าเริ่มต้นที่ส่งไปยัง createContext จะถูกคืนค่า การค้นหานี้เกิดขึ้นทุกครั้งที่เรนเดอร์ แต่ต้องขอบคุณ การจดจำโหนดไฟเบอร์ ทำให้รวดเร็วมากและไม่ส่งผลกระทบต่อประสิทธิภาพ

ตาม React — ภายในบริบท (2024) การทำงานภายในของ useContext ใช้รายการเชื่อมโยงของฮุก คล้ายกับ useState แต่ละฮุกเก็บการอ้างอิงไปยังโหนดไฟเบอร์ ทำให้ React สามารถระบุได้อย่างรวดเร็วว่า Provider ใดสอดคล้องกับบริบทนั้น เมื่อ Provider อัปเดตค่า React จะทำเครื่องหมายโหนดไฟเบอร์ทั้งหมดที่ใช้บริบทนั้นสำหรับการเรนเดอร์ใหม่

Provider และบริบทที่ซ้อนกัน

คอมโพเนนต์ Provider สามารถซ้อนกันภายในกันและกัน สร้างลำดับชั้นของบริบท แต่ละ Provider ย่อยแทนที่ค่าแม่สำหรับซับทรีของตนเอง สิ่งนี้มีประโยชน์เมื่อหน้าจอหนึ่งต้องการธีมสว่างในขณะที่หน้าต่างโมดอลที่ซ้อนกันต้องการธีมมืด useContext จะคืนค่าของ Provider ที่ใกล้ที่สุดด้านบนในแผนผังเสมอ

jsx
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

ฟังก์ชัน createContext(defaultValue) สร้างออบเจ็กต์บริบท พารามิเตอร์ defaultValue ใช้เมื่อคอมโพเนนต์เรียก useContext แต่ไม่มี Provider ที่สอดคล้องกันด้านบนในแผนผัง หากไม่มี defaultValue useContext จะคืนค่า undefined ซึ่งอาจนำไปสู่ข้อผิดพลาดที่ไม่คาดคิด ขอแนะนำให้ส่งค่าเริ่มต้นที่มีความหมายหรือ null เสมอ

การสร้างโพรไวเดอร์แบบกำหนดเองเป็นรูปแบบทั่วไปสำหรับการห่อหุ้มตรรกะบริบท ภายในโพรไวเดอร์ดังกล่าว สถานะจะถูกเก็บ (ผ่าน useState หรือ useReducer) และส่งผ่าน prop value ของ Provider ซึ่งซ่อนรายละเอียดการทำงานจากคอมโพเนนต์ผู้บริโภคและรวมศูนย์ตรรกะการจัดการบริบทไว้ในที่เดียว

jsx
// โพรไวเดอร์แบบกำหนดเองพร้อมการจัดการสถานะ
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 ในคอมโพเนนต์ย่อย

ในคอมโพเนนต์แบบฟังก์ชัน useContext เป็นวิธีเดียวในการเข้าถึงบริบท มันแทนที่คอมโพเนนต์ Consumer จาก Context API เดิม ซึ่งต้องใช้รูปแบบ render-prop: <ThemeContext.Consumer>{value => ...}</ThemeContext.Consumer> useContext ทำให้โค้ดเป็นเชิงเส้นและอ่านง่ายขึ้น โดยเฉพาะเมื่อทำงานกับหลายบริบทในคอมโพเนนต์เดียว

เมื่อใช้หลายบริบทในคอมโพเนนต์เดียว เพียงเรียก useContext หลายครั้งสำหรับแต่ละบริบท แต่ละการเรียกคืนค่าของ Provider ที่สอดคล้องกัน ลำดับการเรียกไม่สำคัญ เนื่องจากแต่ละบริบทเป็นเอนทิตีอิสระ React ปรับการเรียกหลายครั้งให้เหมาะสมผ่านระบบการอ้างอิงไฟเบอร์เดียวกัน

jsx
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 และ Redux ขึ้นอยู่กับขนาดและความซับซ้อนของการจัดการสถานะ useContext + useReducer เป็นตัวแทนน้ำหนักเบาของ Redux สำหรับแอปพลิเคชันขนาดเล็กและขนาดกลาง ไม่ต้องติดตั้งไลบรารีภายนอก เรียนรู้ง่าย และเพียงพอสำหรับงานส่วนใหญ่ Redux เหมาะสมเมื่อต้องการสถาปัตยกรรมที่เข้มงวดกับ middleware เครื่องมือสำหรับนักพัฒนา และการอัปเดตที่ไม่เปลี่ยนสภาพ

ข้อได้เปรียบหลักของ Redux เหนือ useContext คือการปรับการเรนเดอร์ใหม่ให้เหมาะสม โดยค่าเริ่มต้น เมื่อค่าใน Provider เปลี่ยนแปลง ผู้บริโภคบริบททั้งหมดจะเรนเดอร์ใหม่ Redux กับ useSelector และ shallowEqual อนุญาตให้คอมโพเนนต์สมัครรับเฉพาะส่วนเฉพาะของสถานะ ซึ่ง ลดจำนวนการเรนเดอร์ใหม่ในแอปพลิเคชันขนาดใหญ่ได้อย่างมาก บริทยังสามารถปรับให้เหมาะสมโดยการแบ่งเป็นบริบทเล็ก ๆ หลายอัน

เกณฑ์useContextRedux
ความซับซ้อนไม่มีการพึ่งพาภายนอกต้องตั้งค่า store และ middleware
การเรนเดอร์ใหม่ผู้บริโภคทั้งหมดเมื่อมีการเปลี่ยนแปลงเฉพาะที่สมัครรับ slice เฉพาะ
DevToolsReact DevToolsRedux DevTools ที่ย้อนเวลาได้
Middlewareไม่รองรับRedux Thunk, Saga, Observable
เมื่อใดควรเลือกแอปขนาดกลาง 3–5 บริบทแอปขนาดใหญ่ที่มีตรรกะทางธุรกิจซับซ้อน

ตาม ผู้ดูแล Redux — เมื่อใดควรใช้ Redux (2024) 70% ของแอปพลิเคชัน React ไม่จำเป็นต้องใช้ Redux หากคุณมีคอมโพเนนต์น้อยกว่า 50 ตัวและสถานะไม่มีตรรกะซับซ้อนกับการแคช debounce และเอฟเฟกต์ข้างเคียง — useContext + useReducer ก็เพียงพอแล้ว Redux เพิ่มโค้ดเทมเพลตและควรใช้อย่างมีสติ

ข้อผิดพลาดทั่วไปกับ useContext

ข้อผิดพลาดที่พบบ่อยที่สุดคือ การสร้างออบเจ็กต์ value ใหม่ ทุกครั้งที่ Provider เรนเดอร์ หากคุณส่ง value={{ user, login }} ให้ Provider ออบเจ็กต์ใหม่จะถูกสร้างขึ้นทุกครั้งที่ Provider เรนเดอร์ ทำให้ผู้บริโภคทั้งหมดเรนเดอร์ใหม่แม้ว่าข้อมูลจะไม่เปลี่ยนแปลง วิธีแก้คือจดจำค่าด้วย useMemo หรือใช้บริบทแยกสำหรับข้อมูลที่เปลี่ยนแปลงบ่อยและนานครั้ง

  • การเรนเดอร์ใหม่ที่ไม่จำเป็น — ออบเจ็กต์ value ใหม่ทุกครั้งที่ Provider เรนเดอร์ ใช้ useMemo เพื่อจดจำค่า
  • บริบทใหญ่เกินไป — Provider เดียวกับหลายสิบฟิลด์ทำให้คอมโพเนนต์ย่อยทั้งหมดเรนเดอร์ใหม่เมื่อฟิลด์ใด ๆ เปลี่ยนแปลง แบ่งเป็นหลายบริบทตามความหมาย
  • ขาด defaultValue — หากไม่พบ Provider useContext จะคืนค่า defaultValue และถ้าเป็น undefined ทุกการเรียกจะทำให้เกิด TypeError
  • Provider ซ้อนกันชนิดเดียวกัน — การแทนที่บริบทในระดับลึกอาจทำให้สับสนและนำไปสู่ค่าที่ไม่คาดคิด
jsx
// ❌ ออบเจ็กต์ใหม่ทุกครั้งที่เรนเดอร์ — ผู้บริโภคทั้งหมดเรนเดอร์ใหม่
<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 กับ Redux?

สำหรับสถานการณ์ง่าย ๆ useContext เร็วกว่าเนื่องจากไม่มีโอเวอร์เฮดจาก store และ middleware แต่ด้วยการอัปเดตบ่อยครั้งและผู้บริโภคจำนวนมาก Redux ชนะเพราะตัวเลือก (useSelector) ของมันสมัครรับเฉพาะส่วนเฉพาะของสถานะ ในขณะที่ useContext เรนเดอร์ผู้บริโภคทั้งหมดใหม่เมื่อมีการเปลี่ยนแปลงใด ๆ สำหรับแอปพลิเคชันที่มีความถี่ในการอัปเดตสูง (แอนิเมชัน, เรียลไทม์) ให้เลือก Redux หรือไลบรารีเฉพาะทาง

สามารถใช้ useContext นอกคอมโพเนนต์ React ได้หรือไม่?

ไม่ได้ useContext เช่นเดียวกับฮุกทั้งหมด สามารถเรียกได้ภายในคอมโพเนนต์แบบฟังก์ชัน React หรือฮุกที่กำหนดเองเท่านั้น หากคุณต้องการรับค่าบริบทในฟังก์ชันปกติ (เช่น ในยูทิลิตีหรือบริการ) ให้ส่งเป็นพารามิเตอร์จากคอมโพเนนต์หรือใช้โมดูลแยกต่างหากกับสถานะส่วนกลางภายนอก React

useContext ทำงานกับ TypeScript อย่างไร?

การกำหนดชนิดบริบทใน TypeScript ทำได้โดยระบุชนิดใน createContext: createContext<AuthContextType | null>(null) สิ่งนี้รับประกันว่า useContext(AuthContext) คืนค่าชนิดที่ถูกต้อง รูปแบบที่สะดวกคือการสร้างฮุกแบบกำหนดเอง useAuth ที่เรียก useContext ตรวจสอบ null และแสดงข้อผิดพลาดที่ชัดเจน: “ต้องใช้ useAuth ภายใน AuthProvider”

ทำไม useContext คืนค่า undefined ทั้งที่มี Provider อยู่?

สาเหตุที่พบบ่อยที่สุดคือคอมโพเนนต์ผู้บริโภค ไม่ได้อยู่ภายใน Provider ที่สอดคล้องกัน ตรวจสอบว่า Provider หุ้มซับทรีทั้งหมดที่ใช้ useContext อยู่ สาเหตุที่สอง — มีการส่งออบเจ็กต์บริบทอื่นให้ Provider: นักพัฒนาสร้างบริบทด้วยการเรียก createContext แต่ใช้ useContext กับอินสแตนซ์อื่นของ createContext

สรุป

  • useContext — ฮุกสำหรับอ่านค่าจากบริบท React กำจัดความจำเป็นในการใช้ props drilling
  • createContext — สร้างออบเจ็กต์บริบทด้วย Provider สำหรับส่งข้อมูลและ defaultValue สำหรับกรณีที่ไม่มี Provider
  • จดจำค่า — ใช้ useMemo สำหรับค่าของ Provider เพื่อหลีกเลี่ยงการเรนเดอร์ใหม่ที่ไม่จำเป็นของผู้บริโภค
  • แบ่งบริบท — แบ่งสถานะส่วนกลางเป็นบริบทเล็ก ๆ หลายอันตามกลุ่มตรรกะ
  • ลำดับชั้น Provider — สามารถซ้อน Provider ชนิดเดียวกันเพื่อแทนที่ค่าในส่วนของแผนผัง
  • useContext + useReducer — ตัวแทนน้ำหนักเบาของ Redux สำหรับแอปขนาดกลางที่ไม่มีการพึ่งพาภายนอก
  • ไม่แทนที่ Redux — สำหรับตรรกะซับซ้อนกับ middleware และการอัปเดตบ่อยครั้ง เลือก Redux กับตัวเลือก

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม