useContext هو خطاف React يوفر للمكونات الوظيفية وصولاً مباشراً إلى البيانات من السياق الذي تم إنشاؤه عبر createContext. يحل السياق في React مشكلة props drilling — تمرير props عبر العديد من المكونات الوسيطة التي لا تستخدم هذه البيانات بنفسها. وفقاً لوثائق React (2025)، يستقبل useContext كائن السياق ويعيد القيمة الحالية المعينة من أقرب Provider للأعلى في شجرة المكونات. عندما تتغير القيمة في Provider، يتم إعادة رسم جميع المكونات التي تستخدم useContext تلقائياً.
الخلاصة
useContext هو خطاف أُضيف في React 16.8 مع بقية الخطافات يسمح بقراءة قيمة من سياق React. السياق هو آلية مدمجة في React مصممة لنقل البيانات عبر شجرة المكونات دون الحاجة لتمرير props يدوياً في كل مستوى. يستبدل useContext مكون Consumer من Context API القديمة ويجعل الكود أكثر إيجازاً وقابلية للقراءة.
تشمل حالات الاستخدام النموذجية للسياق السمات (فاتح/داكن)، الإعدادات الإقليمية والترجمات (i18n)، مصادقة المستخدم، إعدادات التطبيق وأي بيانات عامة أخرى تحتاجها العديد من المكونات في مستويات تداخل مختلفة. يوصي فريق React باستخدام السياق للبيانات العامة لشجرة فرعية من المكونات، ولكن ليس للتطبيق بأكمله.
وفقاً لفريق React — وثائق السياق (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 في شجرة المكونات وينقل القيمة إلى جميع العناصر التابعة بغض النظر عن عمق التداخل.
عندما يواجه React استدعاء useContext، يتنقل عبر شجرة fiber بحثاً عن أقرب Provider لذلك السياق. إذا وُجد Provider، تُعاد قيمته. إذا لم يُوجد Provider، تُعاد القيمة الافتراضية المُمررة إلى createContext. هذا البحث يحدث في كل render، لكن بفضل تخزين عُقد fiber فهو سريع جداً ولا يؤثر على الأداء.
وفقاً لـ React — داخليات السياق (2024)، التنفيذ الداخلي لـ useContext يستخدم قائمة مرتبطة من الخطافات، مشابهاً لـ useState. كل خطاف يخزن مرجعاً لعقدة fiber، مما يسمح لـ React بتحديد أي Provider يتوافق مع ذلك السياق بسرعة. عندما يحدّث Provider قيمته، يضع React علامة على جميع عُقد fiber التي تستخدم ذلك السياق لإعادة الرسم.
يمكن تداخل مكونات 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) وتُوفّر عبر خاصية 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 الاستدعاءات المتعددة عبر نفس نظام مراجع fiber.
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 |
| إعادة الرسم | جميع المستهلكين عند أي تغيير | فقط المشتركون في شريحة محددة |
| أدوات المطورين | React DevTools | Redux DevTools مع السفر عبر الزمن |
| Middleware | غير مدعوم | Redux Thunk, Saga, Observable |
| متى تختار | تطبيقات متوسطة، 3–5 سياقات | تطبيقات كبيرة مع منطق أعمال معقد |
وفقاً لمطوّري Redux — متى تستخدم Redux (2024)، 70% من تطبيقات React لا تحتاج Redux. إذا كان لديك أقل من 50 مكوناً والحالة لا تتضمن منطقاً معقداً مع تخزين مؤقت و debounce وتأثيرات جانبية — فإن useContext + useReducer أكثر من كافٍ. Redux يضيف كوداً样板ياً ويجب استخدامه بوعي.
الخطأ الأكثر شيوعاً هو إعادة إنشاء كائن value في كل render لـ Provider. إذا مررت value={{ user, login }} إلى Provider، يُنشأ كائن جديد في كل render لـ Provider، مما يسبب إعادة رسم جميع المستهلكين حتى لو لم تتغير البيانات. الحل هو تخزين القيمة مؤقتاً عبر useMemo أو استخدام سياقات منفصلة للبيانات التي تتغير بشكل متكرر ونادر.
// ❌ كائن جديد في كل render — جميع المستهلكين يُعاد رسمهم
<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 أسرع بسبب غياب overhead من 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. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا