🧠 آشنایی عمیق با useReducer + TypeScript + Generics (از جونیور تا سنیور)

۱. ایده اصلی: useReducer چیست؟

useReducer یک الگوی مدیریت state بر پایه توابع خالص و actionهاست.

📮 dispatch (پیک)
   ↓ یک نامه (action) می‌فرستد
👨‍💼 reducer (مدیر)
   ↓ تصمیم می‌گیرد چه اتفاقی بیفتد
🧾 state (وضعیت شرکت)

۲. action چیست؟ (معنای payload)

action پیامی است که توضیح می‌دهد چه اتفاقی افتاده، به‌علاوه داده اختیاری درباره آن (payload):

{
  type: "FETCH_SUCCESS",
  payload: products
}
  • type ← نام رویداد (چه اتفاقی افتاد)
  • payload ← داده‌ای که همراه رویداد منتقل می‌شود

📦 payload را مثل محموله داخل یک بسته پستی ببینید. منطق نیست — حمل داده است.

۳. state (ساختار Generic)

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

T یک نوع داده نامعلوم است (Product، User، Category…)، پس این state برای هر APIای کار می‌کند.

۴. action (Discriminated Union)

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

چرا union type؟ چون مدیر فقط سه پیام معتبر را می‌فهمد.

۵. 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 جدید.

۶. چرا Generics (<T>) مهم است

بدون generics، کد فقط برای Product[] کار می‌کند و برای هر API باید دوباره نوشته شود. با generics، T یک جای‌نگهدار نوع است:

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

همان سیستم خودکار سازگار می‌شود.

۷. جریان کامل 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

۸. نمودار سرتاسری

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

۹. نگاه در سطح سنیور

useReducer فقط مدیریت state نیست. یک ماشین حالت متناهی است که در آن actionها رویدادند، reducer قوانین انتقال را نگه می‌دارد و state تصویری لحظه‌ای از سیستم است.

۱۰. چرا payload حیاتی است

بدون payload فقط می‌توانید بگویید «اتفاقی افتاد». با payload می‌توانید داده واقعی را از طریق رویدادها منتقل کنید. FETCH_SUCCESS بدون payload بی‌فایده است؛ با payload به یک به‌روزرسانی معنادار state تبدیل می‌شود.

۱۱. مدل ذهنی نهایی

  • 📮 dispatch = فرستادن پیام
  • 📄 action = پیام + داده اختیاری (payload)
  • 👨‍💼 reducer = تصمیم‌گیرنده
  • 🧾 state = نتیجه نهایی
  • <T> = سیستم نوع انعطاف‌پذیر برای استفاده دوباره

ریپازیتوری نمونه: react-learning-journey در GitHub