إدارة الحالة الشمولية عبر الدمج بين Context API و useReducer
| المحور الهندسي | الاستخدام المنفرد لـ Context API | الدمج بين Context API و useReducer |
|---|---|---|
| أسلوب تحديث البيانات | التعديل المباشر عبر دوال useState الممررة | تمرير دالة dispatch واحدة لإرسال الأحداث المنظمة |
| السيطرة على التغييرات | قد تصبح معقدة وتتشتت دوال التحديث في المكونات | السيطرة كاملة ومركزية داخل دالة Reducer واحدة |
| قابلية التوسع Scalability | مناسب للتطبيقات الصغيرة أو البيانات الثابتة كالثيم | نمط معماري متكامل (Architecture) يناسب التطبيقات الضخمة |
| سهولة تتبع الأخطاء Debugging | صعوبة معرفة المكون المسبب للتغيير الخاطئ | تتبع سهل عبر معرّفات الأحداث (Action Types) |
عندما نصل لمرحلة نحتاج فيها لإدارة حالة معقدة ومركبة على مستوى التطبيق بالكامل (مثل: سلة المشتريات Cart، بيانات المستخدم مع صلاحياته، أو تفضيلات النظام)، فإن استخدام Context API بمفردها مع useState يصبح غير منظم.
الحل الهندسي الاحترافي هو الجمع بين قوتين:
نحدد الشروط الأساسية للـ State والأحداث، ثم ننشئ كائن الـ Context.
| 1 | import { createContext, useReducer } from 'react'; |
| 2 | |
| 3 | // 1. إنشاء الـ Context |
| 4 | export const CartContext = createContext(); |
| 5 | |
| 6 | // 2. دالة الـ Reducer لإدارة سلة المشتريات |
| 7 | function cartReducer(state, action) { |
| 8 | switch (action.type) { |
| 9 | case 'ADD_ITEM': |
| 10 | return { ...state, items: [...state.items, action.payload] }; |
| 11 | case 'CLEAR_CART': |
| 12 | return { ...state, items: [] }; |
| 13 | default: |
| 14 | return state; |
| 15 | } |
| 16 | } |
بدلاً من ربط الكود داخل App.js مباشرة، نغلف الـ Provider داخل مكون مستقل لنظافة الهيكل:
| 1 | export function CartProvider({ children }) { |
| 2 | const [state, dispatch] = useReducer(cartReducer, { items: [] }); |
| 3 | |
| 4 | return ( |
| 5 | // توفير الـ state والـ dispatch معاً لكل الأبناء |
| 6 | <CartContext.Provider value={{ state, dispatch }}> |
| 7 | {children} |
| 8 | </CartContext.Provider> |
| 9 | ); |
| 10 | } |
الآن يستطيع أي مكون في الشجرة إضافة عنصر أو قراءة السلة بأسلوب نظيف جداً:
| 1 | import { useContext } from 'react'; |
| 2 | import { CartContext } from './CartContext'; |
| 3 | |
| 4 | function CourseCard({ course }) { |
| 5 | const { dispatch } = useContext(CartContext); |
| 6 | |
| 7 | const handleAddToCart = () => { |
| 8 | // إرسال حدث الإضافة |
| 9 | dispatch({ type: 'ADD_ITEM', payload: course }); |
| 10 | }; |
| 11 | |
| 12 | return <button onClick={handleAddToCart}>إضافة للسلة</button>; |
| 13 | } |
| 1 | import { useContext } from 'react'; |
| 2 | import { CartContext } from './CartContext'; |
| 3 | |
| 4 | function Header() { |
| 5 | const { state } = useContext(CartContext); |
| 6 | |
| 7 | return <div>عدد الدروس في السلة: {state.items.length}</div>; |
| 8 | } |
ℹ️ معلومة
عندما تتغير الـ State داخل الـ Provider، فإن كل المكونات التي تستخدم useContext(CartContext) تعاد رندرتها، حتى لو كانت هذه المكونات تحتاج دالة dispatch فقط (مثل أزرار الإضافة)!
أنشئ اثنين Context منفصلين: