Too many re-renders. React limits the number of renders to prevent an infinite loop. هذي الرسالة تطلع في Console، وغالبًا معها صفحة بيضاء كاملة. تصيب أي منتج مبني بـ React، يعني أغلب اللي يطلع من Lovable و Bolt و v0 و Cursor. معناها: جزء في الصفحة يغيّر نفسه وهو يترسم، فيترسم من جديد، ويغيّر نفسه مرة ثانية، بلا نهاية، و React يوقفه بالقوة.
إيش يصير بالضبط
كل جزء في الصفحة (مكوّن) يحمل حالة (state)، مثل: القائمة مفتوحة ولا مسكّرة، والسلة فيها كم منتج. لما الحالة تتغيّر، React يرسم الجزء من جديد. هذا طبيعي.
المشكلة لما تغيير الحالة نفسه يصير أثناء الرسم. الرسم يغيّر الحالة، والحالة تطلب رسم، والرسم يغيّر الحالة. React يعد المرات، ولما توصل حد معيّن يوقف ويرمي الخطأ. ولأن الخطأ يصير وقت الرسم، الصفحة كلها توقف، إلا إذا فيه حماية تمسك الخطأ.
السبب الأشهر: قوسين في المكان الغلط
أغلب الحالات سطر واحد. الفرق بين:
onClick={() => setOpen(true)}: يعني «لما المستخدم يضغط، افتح القائمة». هذا الصح.onClick={setOpen(true)}: يعني «افتح القائمة الحين، وأنت ترسم الزر». هذا اللي يسبب الدوامة.
الفرق () => فقط، وهو سهل يضيع لما الذكاء الاصطناعي يعدّل ملف كبير.
أسباب ثانية نشوفها
- تغيير الحالة مباشرة في جسم المكوّن. مثل
if (user) setName(user.name)مكتوب برا أي دالة. كل رسم يغيّر الاسم، وكل تغيير يطلب رسم. - حالة تنسخ قيمة موجودة. الذكاء الاصطناعي يحب يخزّن نسخة من البيانات في حالة ثانية ويحاول يزامنها: المجموع، أو القائمة بعد الفلتر. القيمة اللي تنحسب من قيمة ثانية ما تحتاج حالة: تنحسب وقت الرسم.
- مكوّن يحدّث المكوّن اللي فوقه وهو يترسم. الابن يغيّر حالة الأب وهو يترسم. React يحذّر منه برسالة ثانية:
Cannot update a component while rendering a different component، وإذا صار دوامة يطلع غالبًا كـ Maximum update depth exceeded.
ليش يطلع بعد تعديل صغير
لأن السطر الغلط ممكن يكون في جزء ما يترسم إلا في حالة معيّنة: مستخدم مسجّل، أو سلة فيها منتجات، أو جوال بشاشة صغيرة. فتجرّب الصفحة وتشتغل، وعميلك يفتحها في حالة ثانية ويشوف صفحة بيضاء.
كيف تتأكد بنفسك
- افتح الصفحة واضغط F12، وفي تبويب Console دوّر على الرسالة.
- تحت الرسالة فيه قائمة أسماء (stack). أول اسم يبدأ بحرف كبير ومو من مكتبة، مثل
CartSummary، هو المكوّن المشتبه. - افتح ملف المكوّن ودوّر على كل سطر يبدأ بـ
set: أي واحد مكتوب برا() =>، أو براuseEffect، أو برا دالة ضغط، هو السبب غالبًا. - إذا الخطأ يطلع لمستخدم معيّن فقط، جرّب بنفس حالته: حساب جديد، أو سلة فاضية، أو جوال.
الحل الصح
- إذا التغيير لازم يصير لما المستخدم يسوي شيء: حطه داخل
() =>في الضغط أو الإرسال. - إذا القيمة تنحسب من قيمة ثانية: احسبها وقت الرسم، واحذف الحالة الزايدة.
- إذا التغيير ردة فعل على شيء تغيّر، مثل وصول بيانات: حطه في
useEffectمع قائمة اعتماد صحيحة. وانتبه هنا من خطأ قريب: Maximum update depth exceeded. - وفوق هذا، خلّ في التطبيق حاجز أخطاء (Error Boundary) حول الأجزاء الكبيرة، عشان خطأ في جزء واحد يعرض رسالة مرتّبة بدل صفحة بيضاء للتطبيق كامل.
الحل اللي لا تسويه
بعض أدوات الذكاء الاصطناعي تحاول توقف الدوامة بدل ما تلقى سببها: عدّاد يمنع التغيير بعد مرة أو مرتين، أو متغير «أول مرة»، أو try/catch حول الرسم. الخطأ يختفي، لكن السبب يبقى، والجزء يعرض أحيانًا بيانات قديمة أو ناقصة. إذا شفت تعديل فيه عدّاد أو شرط غريب عشان «يمنع التكرار»، اسأل: ليش يتكرر أصلًا؟
وإذا كل ما صلّحت هالخطأ طلع خطأ ثاني، شوف كيف توقف دوامة الإصلاح.
كيف تطلب من الذكاء الاصطناعي يصلّحه
لا تكتب له «صلّح الصفحة البيضاء» فقط. انسخ له الرسالة كاملة مع قائمة الأسماء اللي تحتها، وسمّ له المكوّن، واطلب منه يشرح لك السبب بجملة وحدة قبل ما يعدّل أي سطر. وقل له بوضوح: لا عدّادات، ولا شروط تمنع التكرار، ولا try/catch حول الرسم. إذا شرح السبب وما فهمته، هذي علامة إنه ما فهمه هو بعد.
إذا الصفحة البيضاء عند عملائك الحين
كل دقيقة صفحة بيضاء تعني عميل ما كمّل. في مكالمة استشارية نشارك الشاشة ونوصل للمكوّن، ونصلّحه معك على المكالمة إذا قدرنا، أو نكتب لك الخطوات. إذا منتجك على Lovable، شوف مشاكل Lovable الأشهر.