انقل البيانات دون prop drilling باستخدام context، مع إبقاء عمليات إعادة تصييره وحدوده تحت السيطرة.
افتح هذا الدرس في Kodokonيشير prop drilling إلى تلك الـ props التي تمرّ عبر ثلاثة أو أربعة مكوّنات وسيطة دون أن تُستخدم فيها، فقط لتصل إلى ورقة في الشجرة. كل طبقة تعبرها تصبح مرتبطة ببيانات لا تعنيها. يحلّ context مشكلة النقل هذه: createContext يُنشئ القناة، ومزوّد ينشر قيمة، و useContext يقرؤها من أي سليل. احتفظ بالفارق الدقيق في ذهنك: context ينقل الحالة، ولا يديرها - القيمة لا تزال تعيش في مكوّن، هنا المزوّد.
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>
);
}عندما تتغير قيمة المزوّد، يُعاد تصيير كل المستهلكين، حتى المتداخلين بعمق - و React.memo لا يحميهم، لأنه يقارن الـ props وليس الـ context. يزيد الفخّ الكلاسيكي الأمور سوءًا: كتابة value={{ theme, setTheme }} تُنشئ كائنًا جديدًا في كل تصيير للمزوّد، ومن ثم مرجعًا جديدًا، ومن ثم إعادات تصيير متسلسلة حتى عندما لم يتحرك theme. لهذا وُجد useMemo أعلاه. أكمل الإعداد بخطّاف وصول يفشل بصوت عالٍ خارج المزوّد.
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، فاعكس البنية: ابنِ العنصر في أعلى الشجرة، حيث تعيش البيانات، ومرّره إلى الأسفل عبر children أو prop مخصص. تصبح الطبقة الوسيطة عامة ولا تمرّ البيانات بعد ذلك عبر أي مكوّن لا يستخدمها.
function App({ currentUser }) {
return (
<Layout
header={<TopBar user={currentUser} />}
content={<Dashboard />}
/>
);
}