Maximum update depth exceeded تطلع في Console، والصفحة غالبًا تعلّق، أو الجوال يسخن ويبطّأ، أو تتعطّل الصفحة كاملة. في وضع التطوير تجي أحيانًا كتحذير طويل يقول إن مكوّن يغيّر حالته داخل useEffect، والـ useEffect إما بدون قائمة اعتماد، أو فيها شيء يتغيّر مع كل رسم. هذا التحذير ما يطلع في النسخة المنشورة، فالصفحة هناك ممكن تعلّق وتكرر الطلبات بدون أي رسالة. باختصار: حلقة ما تنتهي، لكنها تصير بعد الرسم، مو أثناءه.
الفرق بينه وبين Too many re-renders
خطأ Too many re-renders يصير والمكوّن يترسم. هذا يصير بعد الرسم: الصفحة ترسم، بعدها useEffect يشتغل ويغيّر الحالة، والتغيير يطلب رسم جديد، والرسم الجديد يشغّل useEffect مرة ثانية. نفس الدوامة لكن في خطوة متأخرة، عشان كذا أحيانًا الصفحة تبان ثانية أو ثانيتين قبل ما تعلّق.
الخطر اللي ما يبان: الفاتورة
كثير من useEffect في المنتجات المبنية بالذكاء الاصطناعي تجيب بيانات: طلب لـ Supabase، أو لسيرفرك، أو لـ OpenAI. إذا الحلقة فيها طلب، كل دورة تعني طلب جديد. صفحة وحدة مفتوحة عند عميل واحد ممكن ترسل آلاف الطلبات في دقائق.
والأسوأ: إذا الحلقة تنتظر رد من السيرفر في كل دورة، React ما يعتبرها حلقة أصلًا، والرسالة ما تطلع، والطلبات تستمر. النتيجة: حدود خطتك تخلص، أو فاتورة الذكاء الاصطناعي ترتفع، أو قاعدة البيانات تبطّأ لكل الناس. هذا من الأسباب اللي توصلك لـ You exceeded your current quota.
كيف تعرف إن هذا صار عندك: افتح صفحة الاستخدام في لوحة Supabase أو لوحة مزوّد الذكاء الاصطناعي، وشوف رسم الطلبات. قفزة كبيرة في يوم ما جاك فيه زوار أكثر من العادة، أو خط ما ينزل حتى في الليل، علامة إن فيه صفحة تطلب بلا توقف عند أحد.
ليش يصير كثير في الكود اللي يكتبه الذكاء الاصطناعي
- useEffect بدون قائمة اعتماد. يشتغل بعد كل رسم، وإذا غيّر حالة، يطلب رسم، فيشتغل من جديد.
- قائمة فيها شيء جديد كل مرة. كائن أو قائمة أو دالة تنكتب داخل المكوّن، مثل فلتر
{ status: 'active' }، تنخلق من جديد مع كل رسم. React يقارنها بالسابقة ويشوفها مختلفة، حتى لو محتواها نفسه. - حالتين تزامن بعض. حالة تتغيّر فتغيّر الثانية، والثانية تتغيّر فترجع تغيّر الأولى.
- اشتراك يتجدد. اشتراك Supabase Realtime أو مستمع يتسجّل من جديد في كل رسم بدل مرة وحدة.
- تحويل الصفحة داخل useEffect. مثل
navigate('/login')بشرط يبقى صحيح بعد التحويل، فتصير الصفحة ترمي المستخدم بين صفحتين.
كيف تتأكد بنفسك
- اضغط F12، وافتح تبويب Network، وحدّث الصفحة. إذا شفت نفس الطلب يتكرر بلا توقف، هذي الحلقة، حتى لو ما طلعت الرسالة.
- في Console، الرسالة غالبًا تسمّي المكوّن. افتح ملفه ودوّر على كل
useEffect. - لكل
useEffect: هل فيه قائمة في آخره؟ وإيش فيها؟ إذا فيها كائن أو دالة تنكتب داخل المكوّن، هذا المشتبه الأول. - إذا تقدر، ثبّت إضافة React Developer Tools، وفي إعداداتها فعّل خيار إظهار التحديثات (Highlight updates): الجزء اللي يومض بلا توقف هو مصدر الحلقة.
الحل الصح
- اكتب في قائمة الاعتماد القيم اللي فعلًا لازم تشغّل الـ
useEffect، لا أكثر ولا أقل. - الكائنات والدوال اللي في القائمة: انقلها برا المكوّن، أو ثبّتها بـ
useMemoوuseCallback، أو حط في القائمة القيمة البسيطة نفسها، مثلstatusبدل الكائن كامل. - قبل ما تغيّر الحالة، قارن: إذا القيمة الجديدة نفس القديمة، لا تغيّرها.
- القيم اللي تنحسب من قيم ثانية ما تحتاج
useEffectأصلًا: احسبها وقت الرسم. - جلب البيانات: مكتبة مثل TanStack Query تتكفّل بالتكرار والذاكرة، بدل
useEffectمكتوب باليد في كل صفحة.
الحل اللي لا تسويه
الحل اللي يقترحه الذكاء الاصطناعي كثير: يحذف عناصر من قائمة الاعتماد، أو يحط قائمة فاضية []، أو يضيف تعليق يسكّت التنبيه مثل eslint-disable. الحلقة توقف، لكن الصفحة تصير ما تتحدّث لما لازم تتحدّث: تغيّر الفلتر والنتائج ما تتغيّر، أو تسجّل دخول والاسم القديم يبقى. خطأ صامت أسوأ من خطأ يطلع.
قبل ما يوصل لعملائك
إذا الحلقة فيها طلبات لخدمة تدفع عليها، صلّحها اليوم. مكالمة استشارية تكفي غالبًا لخطأ واحد واضح. إذا تشوف هالنوع من الأخطاء في أكثر من صفحة، الإصلاح المباشر يصلّح أصلها. وكيف تكلّفك الطلبات الزايدة كل شهر في درس كم يكلّف تشغيل تطبيقك شهريًا؟.