🧠 فهم useReducer + TypeScript + Generics (دليل معمّق من المبتدئ إلى الخبير)

1. الفكرة الأساسية: ما هو useReducer؟

useReducer نمط لإدارة الحالة مبني على دوال نقية وأفعال (actions).

📮 dispatch (الساعي)
   ↓ يرسل رسالة (action)
👨‍💼 reducer (المدير)
   ↓ يقرر ما يحدث
🧾 state (وضع الشركة)

2. ما هو الـ action؟ (معنى payload)

الـ action رسالة تصف ما حدث، مع بيانات اختيارية عنه (payload):

{
  type: "FETCH_SUCCESS",
  payload: products
}
  • type ← اسم الحدث (ما الذي حدث)
  • payload ← البيانات المرافقة للحدث

📦 فكّر في الـ payload كالبضاعة داخل صندوق التوصيل. ليست منطقًا — بل نقل للبيانات.

3. الحالة (بنية Generic)

type State<T> = {
  data: T | null;
  loading: boolean;
  error: string | null;
};

T نوع بيانات غير معروف (Product أو User أو Category…)، لذا تعمل هذه الحالة مع أي واجهة برمجية.

4. الـ action (Discriminated Union)

type Action<T> =
  | { type: "FETCH_START" }
  | { type: "FETCH_SUCCESS"; payload: T }
  | { type: "FETCH_ERROR"; payload: string };

لماذا union type؟ لأن المدير لا يفهم إلا ثلاث رسائل صالحة.

5. الـ reducer (محرك القرار)

function reducer<T>(state: State<T>, action: Action<T>): State<T> {
  switch (action.type) {
    case "FETCH_START":
      return { ...state, loading: true, error: null };
    case "FETCH_SUCCESS":
      return { data: action.payload, loading: false, error: null };
    case "FETCH_ERROR":
      return { ...state, loading: false, error: action.payload };
    default:
      return state;
  }
}

النموذج الذهني: state + action ← reducer ← state جديدة.

6. لماذا تهم الـ Generics (<T>)

دون generics يعمل الكود مع Product[] فقط ويجب إعادة كتابته لكل واجهة. ومع generics تصبح T عنصرًا نائبًا للنوع:

useFetch<Product[]>
useFetch<Category[]>
useFetch<User[]>

ويتكيف النظام نفسه تلقائيًا.

7. التدفق الكامل لـ useReducer

1. initialState
2. dispatch(FETCH_START)
3. reducer → loading = true
4. API call
5. dispatch(FETCH_SUCCESS)
6. reducer → data filled
7. UI updates

8. مخطط من البداية إلى النهاية

Component
   │
   ▼
dispatch({ type: "FETCH_START" })
   │
   ▼
Reducer → loading = true
   │
   ▼
API Request
   │
   ├── SUCCESS → dispatch(FETCH_SUCCESS) → data = result
   │
   └── ERROR → dispatch(FETCH_ERROR) → error message

9. فهم بمستوى الخبير

useReducer ليس مجرد إدارة حالة. إنه آلة حالات منتهية تكون فيها الأفعال أحداثًا، ويحمل الـ reducer قواعد الانتقال، والحالة لقطة للنظام.

10. لماذا الـ payload حاسم

دون payload لا يمكنك إلا قول «حدث شيء ما». ومع payload يمكنك نقل بيانات حقيقية عبر الأحداث. FETCH_SUCCESS دون payload بلا فائدة؛ ومع payload يصبح تحديثًا ذا معنى للحالة.

11. النموذج الذهني النهائي

  • 📮 dispatch = إرسال رسالة
  • 📄 action = رسالة + بيانات اختيارية (payload)
  • 👨‍💼 reducer = صانع القرار
  • 🧾 state = النتيجة النهائية
  • <T> = نظام أنواع مرن لإعادة الاستخدام

مستودع المثال: react-learning-journey على GitHub