أخطاء شائعة يقع فيها مطورو React وكيفية إصلاحها باحتراف
مقدمة
عند تطوير واجهات المستخدم باستخدام React، قد تبدو بعض الأخطاء بسيطة في البداية، لكنها تؤثر مباشرة في الأداء، واستقرار التطبيق، وتجربة المستخدم. في هذا المقال سنستعرض مجموعة من أكثر الأخطاء شيوعاً التي يقع فيها مطورو React، مع شرح عملي لكيفية تجنبها وكتابة شيفرة أوضح وأسهل في الصيانة.

لا تنسَ أن تغيير المسار يؤدي إلى إلغاء تحميل المكوّن وتحميل آخر
عند استخدام التوجيه داخل تطبيق React، يتم تعريف المسارات غالباً داخل المكوّن Switch. وهذا يعني أن التطبيق يعرض مكوّناً واحداً فقط في كل مرة وفق المسار المطابق.
بناءً على ذلك، عند الانتقال من مسار إلى آخر، يتم إلغاء تحميل المكوّن السابق unmount ثم تحميل المكوّن الجديد mount. هذه النقطة مهمة جداً إذا كنت تعتمد على بيانات تريد الاحتفاظ بها أثناء التنقل بين الصفحات.
متى تظهر المشكلة؟
إذا خزّنت بيانات داخل مكوّن مرتبط بمسار معين، ثم انتقلت إلى مسار آخر، فغالباً ستفقد تلك البيانات عند العودة، لأن المكوّن الأصلي أُزيل من الواجهة ثم أُعيد إنشاؤه من جديد.
كيف تتجنب ذلك؟
- ضع البيانات المشتركة داخل مكوّن أعلى يحتوي المسارات، مثل
App. - استخدم وسائل تخزين دائمة مثل
localStorageأوsessionStorageعند الحاجة. - فكّر في استخدام إدارة حالة مركزية إذا كانت البيانات تُستخدم في أكثر من جزء من التطبيق.
إذا كنت تراقب التنفيذ عبر console.log، فستلاحظ عند كل تغيير في المسار أن المكوّن السابق يُزال ثم يُحمّل مكوّن جديد، وهذا سلوك طبيعي يجب أخذه في الحسبان أثناء تصميم التطبيق.
لا تستخدم صيغة setState بشكل خاطئ
من الأخطاء المتكررة الخلط بين طريقة تحديث الحالة في المكوّنات المعتمدة على الأصناف class components وبين المكوّنات الوظيفية functional components.
في المكوّنات المعتمدة على الأصناف
تكون الحالة state كائناً object في العادة، مثل المثال التالي:
this.state = {
counter: 0
};
وعند استخدام الصيغة المعتمدة على الحالة السابقة لتحديثها، تكون الكتابة بالشكل التالي:
this.setState((prevState) => {
return {
counter: prevState.counter + 1
};
});
هنا المتغير prevState هو كائن، لذلك نصل إلى القيمة عبر prevState.counter.
في المكوّنات الوظيفية مع Hooks
عند استخدام useState، قد تكون الحالة قيمة بسيطة وليست كائناً، كما في المثال التالي:
const [counter, setCounter] = useState(0);
في هذه الحالة، تكون القيمة السابقة رقماً مباشراً، وليس كائناً يحتوي خاصية باسم counter. لذلك تكون الصيغة الصحيحة:
setCounter((prevCounter) => prevCounter + 1);
ويمكن اختصارها أكثر إلى:
setCounter((counter) => counter + 1);
لماذا هذا الخطأ شائع؟
لأن كثيراً من المطورين ينتقلون من أسلوب class إلى Hooks مع الاحتفاظ بنفس طريقة التفكير. لكن في useState، نوع البيانات هو الذي يحدد طريقة التعامل معها، وليس نمط واحد ثابت.
لا تستدعِ Hooks داخل المكوّنات المعتمدة على الأصناف
منذ الإصدار 16.8.0، قدمت React مفهوم Hooks لتسهيل إدارة الحالة ودورة حياة المكوّنات داخل المكوّنات الوظيفية.
ومن أشهر هذه الأدوات:
useParamsللوصول إلى معاملات الرابط.useHistoryأو البدائل الحديثة للتعامل مع سجل التنقل.useRefللوصول إلى عناصرDOM.
لكن القاعدة الأساسية هنا واضحة: جميع Hooks تعمل فقط داخل المكوّنات الوظيفية أو داخل custom hooks، ولا يمكن استخدامها داخل مكوّنات class.
ما الحل إذا كان المشروع قديماً؟
إذا كنت تعمل على شيفرة قديمة مبنية باستخدام class components وتحتاج إلى استخدام Hooks، فالحل الأفضل هو إعادة هيكلة المكوّن وتحويله إلى مكوّن وظيفي. هذا التغيير لا يسهّل فقط استخدام Hooks، بل يجعل الشيفرة غالباً أبسط وأوضح.

لا تنسَ إضافة الخاصية key عند استخدام map
عند عرض قائمة عناصر في React، من الشائع استخدام الدالة map() للتكرار على المصفوفة وتحويل كل عنصر إلى مكوّن مرئي.
على سبيل المثال:
const Items = ({ items }) => (
<ol>
{items.map((item) => (
<Item item={item} />
))}
</ol>
);
هذه الصيغة ستؤدي إلى ظهور تحذير في وحدة التحكم، لأن كل عنصر ناتج عن map() يجب أن يمتلك خاصية key فريدة.
لماذا تحتاج React إلى key؟
تعتمد React على الخاصية key لمعرفة العناصر التي تغيّرت أو أُضيفت أو أُزيلت، وبالتالي تحسين عملية إعادة التصيير re-rendering وتجنب العمليات غير الضرورية.
الصيغة الصحيحة
<Item item={item} key={index} />
بهذه الطريقة يختفي التحذير، وتتمكن React من تتبع العناصر بشكل صحيح.


ملاحظة مهمة حول استخدام index
استخدام index كمفتاح قد يكون مقبولاً إذا كانت العناصر ثابتة الترتيب ولا تُحذف ولا يُعاد ترتيبها. أما إذا كانت القائمة ديناميكية، فمن الأفضل استخدام معرّف فريد حقيقي مثل id، لأن ذلك يمنع أخطاء العرض غير المتوقعة.
لا تستخدم الدوال المضمّنة داخل الأحداث بطريقة خاطئة
من الأخطاء الشائعة أيضاً تمرير نتيجة استدعاء الدالة إلى الحدث بدلاً من تمرير دالة نفسها. هذا الخطأ قد يؤدي إلى تنفيذ الدالة فور التصيير بدلاً من انتظار نقر المستخدم.
لنفترض أن الحالة معرفة بالشكل التالي:
const [items, setItems] = useState(["one", "two"]);
ثم يتم عرض العناصر هكذا:
{items.map((item, index) => (
<li key={index}>
{item}
<button onClick={handleRemoveItem(item)}>Remove</button>
</li>
))}
في هذا المثال، لن يعمل السلوك كما هو متوقع، لأن التعبير handleRemoveItem(item) يُنفّذ مباشرة أثناء التصيير.
متى تمرر الدالة مباشرة؟
إذا كانت الدالة لا تحتاج إلى معاملات، فاستخدم:
<button onClick={handleRemoveItem}>Remove</button>
متى تستخدم دالة مضمنة inline function؟
إذا كنت بحاجة إلى تمرير قيمة محددة، فاستخدم دالة سهمية:
<button onClick={() => handleRemoveItem(item)}>Remove</button>
بهذا الأسلوب، لن تُنفذ الدالة إلا عند النقر على الزر، وهو السلوك الصحيح.

أفضل الممارسات لتجنب هذه الأخطاء في مشاريع React
- افصل بين منطق الواجهة ومنطق البيانات بوضوح.
- اختر مكان تخزين الحالة بعناية، خاصة إذا كانت مطلوبة عبر أكثر من مسار.
- استخدم
Hooksفقط ضمن السياق الصحيح. - أضف الخاصية
keyلكل عنصر متكرر داخلmap(). - راجع معالجات الأحداث وتأكد أنك تمرر دالة لا نتيجة تنفيذها.
- راقب التحذيرات داخل وحدة التحكم، لأن
Reactيقدم إشارات مفيدة جداً أثناء التطوير.
ملخص سريع للأخطاء والحلول
| الخطأ | السبب | الحل |
|---|---|---|
| فقدان البيانات عند تغيير المسار | إلغاء تحميل المكوّن عند الانتقال | رفع الحالة إلى مكوّن أعلى أو استخدام تخزين دائم |
استخدام خاطئ لـ setState |
الخلط بين class وHooks |
التعامل مع القيمة السابقة حسب نوعها الحقيقي |
استدعاء Hooks داخل class |
مخالفة قواعد React |
تحويل المكوّن إلى مكوّن وظيفي |
غياب key في القوائم |
عدم قدرة React على تتبع العناصر بدقة |
استخدام مفتاح فريد لكل عنصر |
تنفيذ الدالة مباشرة داخل onClick |
تمرير ناتج الاستدعاء بدل الدالة | استخدام دالة مباشرة أو دالة سهمية عند الحاجة إلى معاملات |

الخلاصة التقنية
معظم أخطاء React الشائعة لا ترتبط بصعوبة المكتبة نفسها، بل بسوء فهم دورة حياة المكوّنات، وآلية تحديث الحالة، وطريقة تعامل React مع القوائم والأحداث. كلما التزمت بقواعد Hooks، وأدرت الحالة من مكان مناسب، وكتبت مكوّنات واضحة ذات مسؤوليات محددة، حصلت على تطبيق أكثر استقراراً وأسهل في التوسع والصيانة. الاهتمام بهذه التفاصيل الصغيرة يصنع فرقاً كبيراً في جودة المشروع النهائي.