أهلاً بيك يا صديقي المبرمج في مقال جديد من سلسلة شروحات تطوير الويب (Web Development). تعال نتخيل مع بعض السيناريو ده: قاعد قدام الشاشة بتكتب كود تفاعل مستخدم (User Interface) لموقع كبير، وشغال تمام بـ (useState). وفجأة، المشروع كبر، وبقى عندك فورم (Form) طويلة عريضة، فيها شروط تعتمد على بعضها، وداتا بتيجي من الأبي آي (API)، وحالة تحميل (Loading State)، وحالة خطأ (Error State)، وحايات تانية كتير متشابكة.
تلاقي نفسك كاتب عشرة (useState) جوه نفس الكومبوننت (Component)، وكل شوية تعمل (setState) هنا وهنا، والكود بقى عامل زي "صينية الكنافة" المتباكة اللي مش عارف تفك خيوطها! هنا بالظبط بيظهر الوجع، وبتسأل نفسك: "هو أنا ماشي صح ولا إيه؟". الإجابة ببساطة: لا يا صديقي، إنت محطة وصولك وصلت، ووقت استخدام (useReducer) جه حالا عشان ينقذ الموقف ويدير الـ (Complex States) باحترافية شديدة شبه (Redux) بس محلياً (Locally) جوه الكومبوننت من غير تعقيد.
Table of contents [Show]
إمتى استخدام useState بيكون مش كافي؟ (When useState is Not Enough)
في بداية أي مشروع رياكت (React)، بنحب الـ (useState) جداً؛ خفيفة، سريعة، ومباشرة. بس فيه علامات واضحة بتصرخ في وشك وتقولك "أنا كفاية كده إديني بديل":
- لما يكون عندك (State) بتعتمد في تحديثها على (State) تانية قديمة أو مرتبطة بيها ارتباط وثيق.
- لما تلاقي نفسك بتعمل (setState) في كذا مكان مختلف لنفس الـ (Feature)، والكود بقى متناثر ومش مركز.
- لما يكون شكل الـ (State) عبارة عن (Object) كبير جواه (Nested Data) معقدة، وتحديث جزء صغير منها بيخليك تكتب كود (Spread Operator) طويل وعريض يخوف.
- لما تحس إن منطق التحديث (Update Logic) بقى متشابك جوه الـ (UI Logic) وصعب تعمل له (Unit Testing) لوحده.
لو الحاجات دي ظهرت معاك، يبقى (useReducer) هو البطل المنتظر اللي هيحل لك المشكلة دي كلها.
إيه هو useReducer وإزاي بيشتغل؟ (What is useReducer)
باختصار شديد، (useReducer) هو هوك (Hook) في رياكت (React) مستوحى من فكرة (Redux). الفكرة كلها بتدور حوالين تلات حاجات رئيسية لازم تفهمهم كويس:
- الـ State: دي البيانات الحالية بتاعتك (زي البيانات اللي بتخزنها في useState).
- الـ Action: ده كائن (Object) بيوصف "إيه اللي حصل؟" أو "ايه القرار اللي العميل أخده؟"، وعادة بيكون ليه خاصية (type) وأحياناً (payload) فيه الداتا الجديدة.
- الـ Reducer Function: دي الدالة السحرية اللي بتاخد الـ (State) القديمة والـ (Action)، وتقرر بناءً عليهم ترجع (State) جديدة شكلها إيه. ممنوع منعاً باتاً تغير الـ (State) القديمة بشكل مباشر (Mutate)، لازم ترجع كائن جديد (Pure Function).
مثال عملي: تحويل Form معقدة من useState لـ useReducer
عشان الصورة توضح أكتر، تعال نبص على مثال كود لنموذج تسجيل (Registration Form) معقد شوية، ونشوف إزاي هنكتبه باستخدام (useReducer):
import React, { useReducer } from 'react';
// 1. تحديد الحالة الأولية (Initial State)
const initialState = {
username: '',
email: '',
password: '',
isSubmitting: false,
error: null,
isSuccess: false
};
// 2. كتابة دالة الـ Reducer
function formReducer(state, action) {
switch (action.type) {
case 'SET_FIELD':
return {
...state,
[action.field]: action.value
};
case 'START_SUBMIT':
return { ...state, isSubmitting: true, error: null };
case 'SUBMIT_SUCCESS':
return { ...state, isSubmitting: false, isSuccess: true };
case 'SUBMIT_ERROR':
return { ...state, isSubmitting: false, error: action.payload };
case 'RESET_FORM':
return initialState;
default:
return state;
}
}
function SignupForm() {
// 3. استخدام useReducer داخل الكومبوننت
const [state, dispatch] = useReducer(formReducer, initialState);
const handleChange = (e) => {
dispatch({
type: 'SET_FIELD',
field: e.target.name,
value: e.target.value
});
};
const handleSubmit = async (e) => {
e.preventDefault();
dispatch({ type: 'START_SUBmitting' }); // أو START_SUBMIT
try {
// محاكاة طلب API
await new Promise((resolve) => setTimeout(resolve, 2000));
dispatch({ type: 'SUBMIT_SUCCESS' });
} catch (err) {
dispatch({ type: 'SUBMIT_ERROR', payload: 'حصل خطأ أثناء التسجيل' });
}
};
return (
<form onSubmit={handleSubmit}>
{state.error <p style={{color: 'red'}}>{state.error}</p>}
{state.isSuccess &<p style={{color: 'green'}}>تم التسجيل بنجاح!</p>}
<div>
<input
name="username"
value={state.username}
onChange={handleChange}
placeholder="اسم المستخدم"
/>
</div>
<div>
<input
name="email"
value={state.email}
onChange={handleChange}
placeholder="البريد الإلكتروني"
/>
</div>
<button type="submit" disabled={state.isSubmitting}>
{state.isSubmitting ? 'جاري التسجيل...' : 'تسجيل'}
</button>
</form>
);
}
لو لاحظت في الكود اللي فوق، كل الأحداث وتحديثات الحالة بقت منظمة جوه دالة الـ (Reducer)، وطريقة تحديث البيانات بقت واضحة جداً ومش متناثرة في كل حتة.
امتا تستخدم useReducer وامتا تفضل مع useState؟
مش كل حاجة في الدنيا تروح تعملها (useReducer) وخلاص؛ يعني لو عندك (Toggle) بسيط (True/False) زي فتح وغلق قائمة جانبية (Sidebar)، استخدام (useState) هو الخيار الأسرع والأنسب. لكن لو الـ (State) بتاعتك:
- تتكون من قيم متعددة مترابطة ببعضها.
- الخطوة القادمة في التحديث تعتمد اعتماد مباشر على الحالة الحالية المعقدة.
- عايز تخلي الكود نضيف وقابل للصيانة (Maintainable) وسهل تعمل له (Testing) لوحده برة الكومبوننت.
هنا بقى بصوت عالي قول: أهلاً بـ (useReducer).
نصيحة من أخ لتطوير مهاراتك البرمجية
يا صديقي، الـ (Hooks) المتقدمة زي (useReducer) مش معمولة عشان تعقّب عليك الحياة، دي أدوات محترفة محطوطة في صندوق أدواتك عشان تحل لك مشاكل حقيقية بتظهر مع كبر حجم المشاريع. نصيحتي ليك: متستعجلش وتستخدمها في كل صغيرة وكبيرة، بس في نفس الوقت، متخفش تجربها. اعمل مشروع تجريبي صغير (Coding Challenge) وحاول تدير كل الـ States فيه بـ (useReducer)، مرة على مرة إيدك هتأخد على النمط ده، وهتلاقي نفسك بتكتب كود نظيف، منظم، ومحترف تفتخر بيه قدام أي حد.