دليل المبتدئين إلى React Context: شرح شامل ومتى تستخدمه بذكاء
ما هو React Context؟
يُعد React Context من الأدوات الأساسية التي ينبغي على كل مطور يعمل بـReact فهمها جيداً. وظيفته الرئيسية هي إتاحة مشاركة البيانات أو الحالة state بين مكوّنات التطبيق بسهولة، من دون الحاجة إلى تمريرها يدوياً عبر props في كل مستوى من مستويات الشجرة.
بمعنى أبسط، يوفّر React Context طريقة منظمة للوصول إلى البيانات المشتركة داخل أي مكوّن يحتاجها، حتى لو كان هذا المكوّن متداخلاً بعمق داخل التطبيق.

متى يكون استخدام React Context مناسباً؟
يكون Context مفيداً عندما تكون لديك بيانات عامة نسبياً تحتاجها عدة مكوّنات في أماكن مختلفة من التطبيق. من أبرز الأمثلة على ذلك:
- بيانات المظهر مثل
dark modeوlight mode. - بيانات المستخدم الحالي بعد تسجيل الدخول.
- إعدادات اللغة أو الموقع الجغرافي أو
locale. - الإعدادات العامة التي لا تتغير كثيراً أثناء تشغيل التطبيق.
من المهم الانتباه إلى أن React Context ليس بديلاً كاملاً عن أنظمة إدارة الحالة المتقدمة. هو مناسب أكثر لمشاركة البيانات الثابتة نسبياً أو قليلة التغيير، وليس لإدارة تدفق تحديثات متكررة ومعقدة.
ما المشكلة التي يحلها React Context؟
التخلص من مشكلة props drilling
يساعد React Context على تجنب مشكلة تُعرف باسم props drilling، وهي تمرير props عبر عدة مكوّنات وسيطة فقط لكي تصل في النهاية إلى مكوّن داخلي يحتاجها فعلاً.
هذا النمط يجعل الكود أكثر تشعباً وأصعب في الصيانة، لأن بعض المكوّنات تستقبل بيانات لا تستخدمها فعلياً، بل تمررها فقط إلى مكوّنات أخرى.
إليك مثالاً يوضح هذه المشكلة:
export default function App({ theme }) {
return (
<>
>
);
}
function Header({ theme }) {
return (
<>
>
);
}
في هذا المثال، المكوّن Header لا يحتاج إلى القيمة theme بشكل مباشر، لكنه مضطر لاستقبالها فقط كي يمررها إلى مكوّنات أبنائه مثل User وLogin وMenu.
هنا تظهر فائدة React Context؛ إذ يمكن للمكوّنات التي تحتاج البيانات فعلاً أن تقرأها مباشرة من context من دون المرور عبر المكوّنات الوسيطة.
كيف تستخدم React Context؟
توفّر React واجهة API مدمجة لإنشاء context واستخدامه، وذلك بدءاً من الإصدار 16. وغالباً ما تمر عملية الاستخدام بأربع خطوات رئيسية:
- إنشاء
contextباستخدام الدالةcreateContext(). - تغليف شجرة المكوّنات بالمكوّن
Provider. - تمرير القيمة المراد مشاركتها عبر الخاصية
value. - قراءة القيمة داخل أي مكوّن باستخدام
ConsumerأوuseContext.
مثال عملي على إنشاء Context
في المثال التالي سنمرر اسم مستخدم من المكوّن App إلى المكوّن User باستخدام Context:
import React from 'react';
export const UserContext = React.createContext();
export default function App() {
return (
);
}
function User() {
return (
{value => {value}
}
);
}
شرح المثال خطوة بخطوة
- أنشأنا كائناً جديداً عبر
React.createContext()وخزّناه داخل المتغيرUserContext. - الكائن الناتج يحتوي عادة على مكوّنين مهمين:
ProviderوConsumer. - قمنا بتغليف المكوّن
UserداخلUserContext.Provider. - مررنا القيمة
Reedمن خلال الخاصيةvalue. - داخل المكوّن
UserاستخدمناUserContext.Consumerلقراءة القيمة وعرضها.
ميزة هذا الأسلوب أنه يتيح للمكوّنات قراءة البيانات المشتركة مباشرة، من دون الحاجة إلى تمريرها في كل مستوى عبر props.
ما هو useContext؟
رغم أن استخدام Consumer صحيح وفعال، فإن كثيراً من المطورين يفضلون أسلوباً أبسط وأكثر وضوحاً. مع ظهور React Hooks في الإصدار 16.8 أصبح بالإمكان استهلاك context باستخدام useContext.
بدلاً من الاعتماد على نمط render props، يمكن تمرير كائن context بالكامل إلى React.useContext() ثم استخدام القيمة مباشرة داخل المكوّن.
import React from 'react';
export const UserContext = React.createContext();
export default function App() {
return (
);
}
function User() {
const value = React.useContext(UserContext);
return {value}
;
}
لماذا يفضّل كثيرون useContext؟
- يجعل المكوّنات أكثر اختصاراً ووضوحاً.
- يقلل التشعب في البنية البرمجية مقارنة باستخدام
Consumer. - يسهّل إنشاء
custom hooksقابلة لإعادة الاستخدام.
إذا كنت تعمل في مشروع حديث يعتمد على Hooks، فغالباً سيكون useContext هو الخيار الأبسط.
قد لا تحتاج إلى Context من الأصل
من الأخطاء الشائعة أن يلجأ بعض المطورين إلى Context فور ملاحظتهم أن البيانات تمر عبر أكثر من مستوى. لكن في بعض الحالات يمكن حل المشكلة بطريقة أبسط عبر تحسين تصميم المكوّنات.
لننظر إلى المثال التالي:
export default function App({ user }) {
const { username, avatarSrc } = user;
return (
);
}
function Navbar({ username, avatarSrc }) {
return (
);
}
function Avatar({ username, avatarSrc }) {
return
;
}
هنا يتم تمرير الخاصيتين username وavatarSrc عبر المكوّن Navbar فقط للوصول إلى Avatar. قبل استخدام Context، من الأفضل التفكير في إعادة تركيب المكوّنات.
حل أبسط عبر component composition
export default function App({ user }) {
const { username, avatarSrc } = user;
const avatar =
;
return (
);
}
function Navbar({ avatar }) {
return ;
}
بهذا الأسلوب، خفّضنا عدد props الممررة وجعلنا البنية أوضح. الخلاصة هنا: لا تستخدم Context تلقائياً، بل قيّم أولاً ما إذا كانت إعادة تنظيم المكوّنات كافية لحل المشكلة.
هل يَحل React Context محل Redux؟
الإجابة المختصرة: نعم ولا.
إذا كانت حاجتك الأساسية هي تمرير بيانات مشتركة داخل الشجرة، فإن React Context قد يكون كافياً تماماً، ولا حاجة لإضافة مكتبة خارجية مثل Redux.
لكن إذا كان تطبيقك يحتوي على تحديثات كثيرة، ومنطق حالة معقد، وتدفق بيانات واسع، فقد تكون أدوات إدارة الحالة المتخصصة أكثر ملاءمة. فـRedux لا يقتصر على نقل البيانات، بل يقدّم نموذجاً أكثر صرامة وتنظيماً لإدارة الحالة على مستوى التطبيقات الكبيرة.
محاذير مهمة عند استخدام React Context
التحديثات المتكررة قد تؤثر في الأداء
يمكن من الناحية التقنية دمج React Context مع أدوات مثل useReducer لبناء نظام إدارة حالة بسيط من دون مكتبات خارجية. لكن هذا لا يكون الخيار الأفضل دائماً من ناحية الأداء.
السبب هو أن تحديث القيمة داخل Provider قد يؤدي إلى إعادة تصيير re-render لكل المكوّنات التي تستهلك هذا context. وإذا كانت القيمة كائناً object يحتوي على عدة خصائص وتتغير إحداها باستمرار، فقد ينعكس ذلك على أداء التطبيق.
قد لا تكون هذه المشكلة واضحة في التطبيقات الصغيرة أو عندما تكون البيانات قليلة التغير مثل theme. لكنها تصبح أكثر حساسية في الحالات التالية:
- وجود عدد كبير من المكوّنات المستهلكة لنفس
context. - تحديث الحالة بشكل متكرر وسريع.
- اعتماد التطبيق على شجرة مكوّنات عميقة ومعقدة.
لذلك يُنصح باستخدام Context بحكمة، وتخصيصه للبيانات العامة التي لا تتغير كثيراً، بدلاً من تحميله كل منطق إدارة الحالة في المشروع.
أفضل الممارسات عند العمل مع React Context
- استخدمه للبيانات المشتركة واسعة النطاق، لا لكل حالة محلية.
- قسّم
contextإلى أكثر من واحد إذا كانت البيانات غير مترابطة. - تجنب تخزين قيم كثيرة التحديث داخله ما لم تكن مضطراً.
- ابدأ أولاً بحل التصميم البسيط عبر تحسين تركيب المكوّنات.
- اعتمد
useContextفي المشاريع الحديثة لسهولة القراءة والصيانة.
الخلاصة التقنية
يُعتبر React Context حلاً عملياً وأنيقاً لمشاركة البيانات بين المكوّنات وتفادي مشكلة props drilling، خاصة عند التعامل مع إعدادات عامة مثل المستخدم الحالي أو المظهر أو اللغة. ومع ذلك، لا ينبغي استخدامه كخيار افتراضي لكل مشكلة تتعلق بتمرير البيانات. الاستخدام الذكي لـContext يبدأ بفهم طبيعة البيانات ومعدل تحديثها وحجم التطبيق. فإذا استُخدم في موضعه الصحيح، فإنه يمنحك كوداً أنظف وتجربة تطوير أكثر سلاسة.