useContext: الجوهر، خطاف الوصول إلى السياق والمزوّدات في React

المؤلف: IT Sectr نُشر: 2026-07-04 وقت القراءة: 9 دق

useContext هو خطاف React يوفر للمكونات الوظيفية وصولاً مباشراً إلى البيانات من السياق الذي تم إنشاؤه عبر createContext. يحل السياق في React مشكلة props drilling — تمرير props عبر العديد من المكونات الوسيطة التي لا تستخدم هذه البيانات بنفسها. وفقاً لوثائق React (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 — وثائق السياق (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 في شجرة المكونات وينقل القيمة إلى جميع العناصر التابعة بغض النظر عن عمق التداخل.

عندما يواجه React استدعاء useContext، يتنقل عبر شجرة fiber بحثاً عن أقرب Provider لذلك السياق. إذا وُجد Provider، تُعاد قيمته. إذا لم يُوجد Provider، تُعاد القيمة الافتراضية المُمررة إلى createContext. هذا البحث يحدث في كل render، لكن بفضل تخزين عُقد fiber فهو سريع جداً ولا يؤثر على الأداء.

وفقاً لـ React — داخليات السياق (2024)، التنفيذ الداخلي لـ useContext يستخدم قائمة مرتبطة من الخطافات، مشابهاً لـ useState. كل خطاف يخزن مرجعاً لعقدة fiber، مما يسمح لـ React بتحديد أي Provider يتوافق مع ذلك السياق بسرعة. عندما يحدّث Provider قيمته، يضع React علامة على جميع عُقد fiber التي تستخدم ذلك السياق لإعادة الرسم.

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) وتُوفّر عبر خاصية 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 الاستدعاءات المتعددة عبر نفس نظام مراجع fiber.

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
إعادة الرسمجميع المستهلكين عند أي تغييرفقط المشتركون في شريحة محددة
أدوات المطورينReact DevToolsRedux DevTools مع السفر عبر الزمن
Middlewareغير مدعومRedux Thunk, Saga, Observable
متى تختارتطبيقات متوسطة، 3–5 سياقاتتطبيقات كبيرة مع منطق أعمال معقد

وفقاً لمطوّري Redux — متى تستخدم Redux (2024)، 70% من تطبيقات React لا تحتاج Redux. إذا كان لديك أقل من 50 مكوناً والحالة لا تتضمن منطقاً معقداً مع تخزين مؤقت و debounce وتأثيرات جانبية — فإن useContext + useReducer أكثر من كافٍ. Redux يضيف كوداً样板ياً ويجب استخدامه بوعي.

الأخطاء الشائعة مع useContext

الخطأ الأكثر شيوعاً هو إعادة إنشاء كائن value في كل render لـ Provider. إذا مررت value={{ user, login }} إلى Provider، يُنشأ كائن جديد في كل render لـ Provider، مما يسبب إعادة رسم جميع المستهلكين حتى لو لم تتغير البيانات. الحل هو تخزين القيمة مؤقتاً عبر useMemo أو استخدام سياقات منفصلة للبيانات التي تتغير بشكل متكرر ونادر.

  • إعادة رسم غير ضرورية — كائن value جديد في كل render لـ Provider. استخدم useMemo لتخزين القيمة مؤقتاً.
  • سياق كبير جداً — Provider واحد بعشرات الحقول يجعل جميع المكونات التابعة تُعاد رسمها عند تغيير أي حقل. قسم إلى عدة سياقات حسب المعنى.
  • عدم وجود defaultValue — إذا لم يُعثر على Provider، سيعيد useContext defaultValue، وإذا كان undefined، فكل استدعاء سيرمي TypeError.
  • مزوّدات متداخلة من نفس النوع — تجاوز السياق في مستويات عميقة قد يكون مربكاً ويؤدي إلى قيم غير متوقعة.
jsx
// ❌ كائن جديد في كل 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 أم Redux؟

للسيناريوهات البسيطة، useContext أسرع بسبب غياب overhead من 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 لتجنب إعادة رسم غير ضرورية للمستهلكين.
  • تقسيم السياقات — قسم الحالة العامة إلى عدة سياقات صغيرة حسب المجموعات المنطقية.
  • تسلسل Providers الهرمي — يمكن تداخل Providers من نفس النوع لتجاوز القيم في جزء من الشجرة.
  • useContext + useReducer — بديل خفيف لـ Redux للتطبيقات المتوسطة بدون تبعيات خارجية.
  • لا يستبدل Redux — لمنطق معقد مع middleware وتحديثات متكررة، اختر Redux مع المحددات.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا