0Pricing
Frontend Academy · درس

useReducer للحالة المعقّدة

نمذج انتقالات الحالة باستخدام دالة reducer، وأرسل كائنات الإجراءات، وفضّل useReducer على useState عندما يزداد تعقيد منطق الحالة.

useReducer للحالة المعقّدة درس مجاني في Frontend Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Frontend Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Frontend Academy 4 دروس في المجموع.

عندما لا يكون useState كافيًا

عندما تتضمن انتقالات الحالة منطقًا معقدًا، أو عدة قيم مترابطة، أو شروطًا تعتمد فيها الحالة الجديدة على السابقة بطرق غير بسيطة، يكون useReducer خيارًا أنظف من استدعاءات useState المتعددة.

نمط دالة Reducer

الـ reducer دالة نقية: (state, action) => newState. تأخذ الحالة الحالية وكائن action، ثم تعيد الحالة التالية. لا تُجرِ أي تعديلات مباشرة — أعد دائمًا كائنًا جديدًا.

type Action =
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'reset'; payload: number };

function counterReducer(state: number, action: Action): number {
  switch (action.type) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    case 'reset':     return action.payload;
    default: return state;
  }
}

صيغة useReducer

تعيد useReducer(reducer, initialState) الحالة الحالية ودالة dispatch. استدعِ dispatch باستخدام كائن action لتشغيل انتقال في الحالة.

function Counter() {
  const [count, dispatch] = useReducer(counterReducer, 0);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Reset</button>
    </div>
  );
}

الإجراءات ذات الأنواع — الاتحادات المميّزة

استخدم أنواع الاتحادات المميّزة في TypeScript للإجراءات، لتحصل على فحص شامل للأنواع داخل تعليمة switch في reducer.

type CartAction =
  | { type: 'ADD_ITEM'; item: CartItem }
  | { type: 'REMOVE_ITEM'; id: string }
  | { type: 'SET_QUANTITY'; id: string; qty: number }
  | { type: 'CLEAR' };

مثال على حالة معقدة: عربة التسوق

تُعد عربة التسوق التي تتضمن عمليات الإضافة والإزالة وتحديث الكمية حالة استخدام مثالية لـ useReducer.

interface CartState {
  items: CartItem[];
  total: number;
}

function cartReducer(state: CartState, action: CartAction): CartState {
  switch (action.type) {
    case 'ADD_ITEM': {
      const exists = state.items.find(i => i.id === action.item.id);
      if (exists) {
        const items = state.items.map(i =>
          i.id === action.item.id ? { ...i, qty: i.qty + 1 } : i
        );
        return { ...state, items, total: calcTotal(items) };
      }
      const items = [...state.items, { ...action.item, qty: 1 }];
      return { ...state, items, total: calcTotal(items) };
    }
    case 'CLEAR': return { items: [], total: 0 };
    default: return state;
  }
}

dispatch ثابت

دالة dispatch ثابتة — فهي لا تتغير بين عمليات التصيير. لذلك من الآمن تمريرها كخاصية أو استخدامها كتبعيات لـ useEffect دون التسبب في إعادة التشغيل.

دمج useReducer وuseContext

نمط قوي يوفّر فيه السياق كلًا من الحالة وdispatch، مما يتيح لأي مستهلك قراءة الحالة أو إرسال الإجراءات دون تمرير الخصائص عبر عدة مستويات.

const CartContext = createContext<{ state: CartState; dispatch: Dispatch<CartAction> } | null>(null);

function CartProvider({ children }: { children: React.ReactNode }) {
  const [state, dispatch] = useReducer(cartReducer, { items: [], total: 0 });
  return <CartContext.Provider value={{ state, dispatch }}>{children}</CartContext.Provider>;
}

useReducer أم useState

اختر useReducer عندما: تحتوي الحالة على قيم فرعية متعددة، أو تعتمد الحالة التالية على السابقة بطرق معقدة، أو تكون الانتقالات كثيرة ومسمّاة بوضوح، أو ترغب في اختبار منطق الحالة بعيدًا عن المكوّن.

اختبار Reducers بشكل مستقل

بما أن reducers دوال نقية، فمن السهل جدًا اختبارها دون تصيير أي مكوّن.

test('increment action increases count', () => {
  expect(counterReducer(0, { type: 'increment' })).toBe(1);
  expect(counterReducer(5, { type: 'increment' })).toBe(6);
});

test('reset action sets count to payload', () => {
  expect(counterReducer(42, { type: 'reset', payload: 0 })).toBe(0);
});

استخدام Immer للتحديثات غير القابلة للتغيير

تتيح لك مكتبة immer كتابة تعليمات تبدو كأنها تعدّل الحالة، بينما تنتج حالة جديدة غير قابلة للتغيير. وهذا مفيد لتحديث الكائنات المتداخلة المعقدة داخل reducers.

تحقق سريع

ما توقيع دالة reducer؟

مراجعة: useReducer

(state, action) => newState — دالة نقية بلا تعديلات مباشرة. تعيد useReducer(reducer, initial) القيمة [state, dispatch]. استخدم أنواع الاتحادات المميّزة للإجراءات لضمان السلامة. dispatch ثابت. وهو مناسب للحالات المعقدة ذات الانتقالات المتعددة. ادمجه مع useContext لإدارة الحالة العامة. واختبر reducers بشكل مستقل بوصفها دوال نقية.

الأسئلة الشائعة

هل درس «useReducer للحالة المعقّدة» مجاني؟

نعم — نص درس «useReducer للحالة المعقّدة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Frontend Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Frontend Academy 4 دروس في المجموع.

ماذا ستتعلم في «useReducer للحالة المعقّدة»؟

نمذج انتقالات الحالة باستخدام دالة reducer، وأرسل كائنات الإجراءات، وفضّل useReducer على useState عندما يزداد تعقيد منطق الحالة. تتمرن على Frontend Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Frontend Academy؟

لا تُشترط خبرة سابقة. Frontend Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «useReducer للحالة المعقّدة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Frontend Academy هذا؟

نعم. كل درس في Frontend Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. useContext للحالة العامة
  2. useReducer للحالة المعقّدة
  3. useMemo وuseCallback لتحسين الأداء
  4. الخطافات المخصّصة: استخراج المنطق القابل لإعادة الاستخدام
← العودة إلى Frontend Academy