TypeError: Cannot read properties of undefined (reading 'map') من أكثر الرسائل اللي تطلع في منتجات React. ممكن تشوفها بكلمة ثانية بدل map، مثل (reading 'length') أو (reading 'name')، أو بـ null بدل undefined. وفي iPhone و Safari تطلع بصيغة ثانية: undefined is not an object (evaluating 'data.map'). كلها تعني نفس الشيء: الكود طلب شيء من بيانات مو موجودة.
إيش يعني بالضبط
map تمر على قائمة وتعرض كل عنصر فيها: الطلبات، المنتجات، الرسائل. الكود افترض إن القائمة موجودة. لما وصل لها، لقاها undefined، يعني ما وصلت أصلًا، أو null، وهذا مع Supabase غالبًا يعني إن الطلب رجع خطأ، لأن القائمة الفاضية ترجع [] مو null. وبما إن ما فيه قائمة، ما فيه شيء يمر عليه، فيتوقف الجزء كامل، وغالبًا الصفحة كلها.
ليش يشتغل عندك ويتعطّل عند العميل
لأنك تجرّب في ظروف مريحة: بيانات تجريبية جاهزة، وإنترنت سريع، وحساب فيه كل شيء. الأسباب اللي نشوفها:
- البيانات ما وصلت بعد. الصفحة ترسم مرة قبل ما يرجع الطلب. على إنترنتك السريع ما تلاحظ، وعلى جوال بشبكة ضعيفة الصفحة تتعطّل في أول ثانية.
- حساب جديد فاضي. عميل جديد ما عنده طلبات، والطلب يرجع شيء غير القائمة اللي يتوقعها الكود.
- الطلب رجع خطأ. مع Supabase، الطلب يرجع
dataوerror. إذا فيه خطأ،dataتكونnull، والكود اللي ما يشوفerrorيكمّل كأن كل شيء تمام. - شكل البيانات تغيّر. السيرفر صار يرجع
{ items: [...] }بدل القائمة مباشرة، بعد تعديل طلبته من الذكاء الاصطناعي في ملف ثاني. - سجلات قديمة. عمود أضفته بعدين، والسجلات القديمة ما فيها قيمة له.
- اسم الحقل غلط. الكود يطلب
user.fullName، والعمود في قاعدة البيانات اسمهfull_name. في المعاينة كانت البيانات مكتوبة باليد بالاسم اللي يتوقعه الكود.
كيف تتأكد بنفسك
- اضغط F12، وفي Console اضغط على اسم الملف جنب الرسالة: يوديك للسطر بالضبط، وتشوف اسم المتغير اللي كان فاضي.
- في تبويب Network، دوّر على الطلب اللي يجيب هالبيانات، وافتح Response: هل هو قائمة؟ ولا رسالة خطأ؟ ولا شيء فاضي؟
- جرّب بحساب جديد ما فيه أي بيانات.
- في تبويب Network، غيّر السرعة من No throttling لسرعة بطيئة مثل Slow 4G أو 3G، وحدّث الصفحة.
الحل الصح
كل صفحة تجيب بيانات لها أربع حالات، ولازم كل وحدة تنعرض صح:
- تحميل: البيانات في الطريق. اعرض مؤشر تحميل، ولا ترسم القائمة.
- خطأ: الطلب فشل. اعرض رسالة واضحة، وسجّل الخطأ عشان توصلك.
- فاضي: ما فيه بيانات فعلًا. اعرض «ما عندك طلبات للحين» مع الخطوة التالية.
- بيانات: اعرض القائمة.
قبل الإطلاق، جرّب كل صفحة مهمة في الحالات الأربع. الفاضي سهل: حساب جديد. التحميل: سرعة بطيئة في Network. الخطأ أصعب شوي: افصل الإنترنت بعد ما تفتح الصفحة واضغط زر يجيب بيانات، أو اطلب من المبرمج يعطيك طريقة تجرّب فيها. أي صفحة تعرض شيء غلط في وحدة من الحالات، هذا مكان الخطأ الجاي.
وفوق هذا: تأكد من error بعد كل طلب لـ Supabase قبل ما تلمس data، وخلّ القيمة الأولى للقائمة [] بدل لا شيء.
الحل اللي لا تسويه
الذكاء الاصطناعي يحب يحل هالخطأ بعلامة استفهام: data?.map أو (data || []).map في كل مكان. الخطأ يختفي، لكن تخيّل إيش يصير لما السبب خطأ صلاحيات: العميل يفتح صفحة طلباته ويشوفها فاضية، ويحسب إن طلباته انحذفت، وأنت ما يوصلك أي خطأ. هذي مو حماية، هذا إخفاء. الحماية الصح تفرّق بين «فاضي» و«صار خطأ».
وإذا السبب فعلًا صلاحيات، شوف permission denied for table و 5 ثغرات في تسجيل الدخول.
إذا الخطأ يتكرر في أكثر من صفحة
خطأ واحد، تقدر تصلّحه بالخطوات اللي فوق. إذا يطلع في صفحات كثيرة، معناه إن المنتج كله ما يتعامل مع حالات التحميل والخطأ والفاضي، وهذا شيء يستاهل يتصلّح مرة وحدة من أصله: كل طلب يمر على مكان واحد يتأكد من الخطأ، وأنواع TypeScript تطابق شكل البيانات الحقيقي، فالمحرر يحذّر قبل ما يوصل الخطأ لعميلك. هذا شغل الإصلاح المباشر. وإذا تبي تعرف كم يكلّف قبل، شوف كم يكلّف إصلاح تطبيق مبني بالذكاء الاصطناعي؟.