ย้ายข้อมูลไปมาโดยไม่ต้อง prop drilling ด้วย context พร้อมทั้งควบคุมการ re-render และข้อจำกัดของมันให้อยู่ในมือ
เปิดบทเรียนนี้ใน KodokonProp drilling หมายถึง props เหล่านั้นที่ผ่าน component คั่นกลางสามหรือสี่ตัวโดยไม่ได้ถูกใช้ที่นั่นเลย เพียงเพื่อไปให้ถึงใบของต้นไม้ ทุกชั้นที่มันผ่านก็จะผูกติดกับข้อมูลที่มันไม่ได้สนใจ Context แก้ปัญหาการขนส่งนี้: createContext สร้างช่องทาง, provider เผยแพร่ค่า และ useContext อ่านมันจาก descendant ตัวใดก็ได้ จำความละเอียดอ่อนนี้ไว้: context ขนส่ง state มันไม่ได้ จัดการ state - ค่ายังคงอยู่ใน component หนึ่ง ในที่นี้คือ provider
import { createContext, useMemo, useState } from "react";
const ThemeContext = createContext(null);
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("dark");
const value = useMemo(
() => ({ theme, setTheme }),
[theme]
);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}เมื่อค่าของ provider เปลี่ยน consumer ทั้งหมดจะ re-render แม้แต่ตัวที่ซ้อนลึก - และ React.memo ก็ไม่ได้ปกป้องพวกมัน เพราะมันเปรียบเทียบ props ไม่ใช่ context กับดักคลาสสิกยิ่งทำให้แย่ลง: การเขียน value={{ theme, setTheme }} สร้าง object ใหม่ในทุก render ของ provider จึงเกิด reference ใหม่ จึงเกิด re-render ต่อเนื่องเป็นลูกโซ่แม้ theme ยังไม่ได้ขยับ นั่นคือหน้าที่ของ useMemo ข้างบน ต่อยอดการตั้งค่าด้วย access hook ที่ล้มเหลวอย่างส่งเสียงดังเมื่ออยู่นอก provider
import { useContext } from "react";
function useTheme() {
const context = useContext(ThemeContext);
if (context === null) {
throw new Error(
"useTheme must be inside <ThemeProvider>"
);
}
return context;
}ก่อนสร้าง context ให้ใช้ตัวเลือก composition จนหมดก่อน ถ้าชั้นคั่นกลางไม่ได้ทำอะไรนอกจากส่งต่อ prop ให้พลิกโครงสร้าง: สร้าง element ไว้ที่ด้านบนของต้นไม้ตรงที่ข้อมูลอยู่ แล้วส่งมันลงไปผ่าน children หรือ prop เฉพาะทาง ชั้นคั่นกลางจะกลายเป็น generic และข้อมูลก็ไม่ผ่าน component ใดที่ไม่ได้ใช้มันอีกต่อไป
function App({ currentUser }) {
return (
<Layout
header={<TopBar user={currentUser} />}
content={<Dashboard />}
/>
);
}