🚀 احتراف إدارة الحالة باستخدام useReducer في React
لقد تعلمت سابقاً كيف تستخدم useState لإدارة الحالة (State)، وهي أداة رائعة وبسيطة. ولكن، ماذا لو أصبحت الحالة "معقدة"؟ ماذا لو كان لديك عدة قيم مرتبطة ببعضها، أو كانت الحالة تتغير بناءً على عمليات منطقية متعددة؟ 🤯
هنا يأتي دور البطل الجديد: useReducer.
🧐 ما هو useReducer وما لماذا نحتاجه؟
useReducer هو "هوك" (Hook) يُستخدم لإدارة الحالة في React، وهو بديل متطور لـ useState. بدلاً من تحديث الحالة مباشرة، يقوم useReducer باتباع نمط محدد يسمى "Reducer Pattern".
ببساطة: تخيل أن useReducer مثل "موظف الاستقبال" في شركة. أنت لا تدخل إلى المكتب وتغير الملفات بنفسك، بل تعطي الموظف "طلب" (Action)، وهو يقوم بتنفيذ التغيير المناسب بناءً على نوع الطلب.
متى نستخدمه بدلاً من useState؟
- عندما تكون الحالة عبارة عن كائن (Object) يحتوي على عدة خصائص.
- عندما تعتمد الحالة القادمة على الحالة السابقة بشكل معقد.
- عندما يكون لديك منطق تحديث يتكرر في أكثر من مكان.
🛠️ المكونات الأساسية لـ useReducer
لكي يعمل useReducer بشكل صحيح، نحتاج إلى ثلاثة عناصر أساسية:
1. الحالة الابتدائية (Initial State) 🚩
وهي القيمة التي يبدأ بها التطبيق (مثلاً: رقم صفر، أو كائن فارغ).
2. دالة الـ Reducer (The Reducer Function) ⚙️
هي دالة تأخذ شيئين: الحالة الحالية (State) و الطلب (Action)، ثم تعيد حالة جديدة.
(state, action) => newState
3. دالة الإرسال (Dispatch Function) 📨
هي الدالة التي نستخدمها لإرسال "الطلب" إلى الـ Reducer لإخباره بأن شيئاً ما قد حدث.
👨💻 مثال عملي: عداد بسيط (Counter)
لنطبق هذه المفاهيم على مثال "العداد" الشهير، ولكن باستخدام useReducer.
import React, { useReducer } from 'react';
// 1. Define the initial state
// تعريف الحالة الابتدائية
const initialState = { count: 0 };
// 2. Define the reducer function
// تعريف دالة الـ reducer التي تحدد كيف تتغير الحالة
function reducer(state, action) {
switch (action.type) {
case 'increment': // إذا كان الطلب هو زيادة
return { count: state.count + 1 };
case 'decrement': // إذا كان الطلب هو نقصان
return { count: state.count - 1 };
case 'reset': // إذا كان الطلب هو تصفير
return { count: 0 };
default:
return state; // في حال لم يتم التعرف على الطلب، ابقِ الحالة كما هي
}
}
function Counter() {
// 3. Initialize useReducer
// تهيئة الـ hook: يعطينا الحالة الحالية ودالة الإرسال
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div style={{ textAlign: 'center', marginTop: '20px' }}>
<h1>Count: {state.count}</h1>
{/* We use dispatch to send an action object */}
{/* نستخدم dispatch لإرسال كائن يحتوي على نوع العملية */}
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</div>
);
}
export default Counter;
🔍 شرح تفصيلي للكود السابق
initialState: بدأنا بكائن{ count: 0 }لأننا نريد تتبع الرقم.reducer(state, action): هذه الدالة هي "العقل المدبر". استخدمنا فيهاswitchللتأكد من نوع العملية (action.type). إذا كان النوعincrementفإنها تعيد نسخة جديدة من الحالة مضافاً إليها 1.dispatch({ type: '...' }): لاحظ أننا لا نقولsetState(count + 1)، بل نقول لـ React: "يا React، أرسلي طلباً للـ Reducer بأن العملية هيincrement"، والـ Reducer هو من يقرر كيف يتم التحديث.
💡 ملاحظات ذهبية للمبتدئين
- عدم التعديل المباشر (Immutability): لاحظ أننا في الـ Reducer لم نكتب
state.count = state.count + 1بل أعدنا كائناً جديداً بالكامل{ count: state.count + 1 }. في React، يجب دائماً إرجاع حالة جديدة وليس تعديل القديمة. - تسمية الـ Actions: يفضل دائماً أن يكون الـ Action عبارة عن كائن يحتوي على خاصية
type(مثل{ type: 'ADD_USER' }) لأن هذا يجعل الكود منظماً وسهل القراءة.
🎯 ملخص سريع
useStateللحالات البسيطة (نص، رقم، بولين).useReducerللحالات المعقدة (كائنات، مصفوفات، عمليات متعددة).
🎓 اختبر معلوماتك
التعليقات
شاركنا رأيك أو أسئلتك حول هذا المقال