معالجة الأخطاء (Error Handling)
الدرس 25 من 34 · دورة TanStack Router الكاملة · 6 دقيقة و25 ثانية
الفيديو الأصلي باللغة الإنجليزية. إذا تعذّر تشغيله هنا، شاهده على موقع المؤلف. التفريغ النصي العربي الكامل أدناه.
مستودع الكود لهذا الدرس
الفرع:
error-handling-1
[00:01] فلنتحدّث قليلًا عن كيفية تعاملنا مع الأخطاء (errors) في تطبيق TanStack Router لدينا.
[00:07] فلنقل مثلًا أنّنا نحاول الوصول إلى تدوينة (post) غير موجودة. سأذهب إذًا إلى posts ثم شريطة مائلة (slash) ومعرّف (ID) لا يوجد؛ وربّما تضع بعض الأرقام أو بعض الحروف هنا.
[00:20] وكما ترى، سنحصل على خطأ fail to fetch post وهو الخطأ الذي نرميه هنا في ملف data/posts في دالة getPost لدينا. في هذا المكان نرمي هذا الخطأ، وهذا ما نحصل عليه
[00:34] هنا لأنّ الـ API في هذه الحالة سيُرجع خطأً. وما يمكننا فعله أنّه يمكننا بالطبع تخصيص صفحة الخطأ (error page) هذه وجعل شكلها يندرج مع تطبيقنا أكثر قليلًا.
[00:46] ولدينا أيضًا استدعاء راجع (callback) سيعمل في حال حدث أيّ خطأ في مسارنا. وفي هذا الاستدعاء الراجع يمكننا ربّما الإبلاغ عن الخطأ إلى خدمةٍ ما لتقارير الأخطاء مثلًا. فلنذهب إلى هذا المسار. وبالطبع، ما
[00:58] سنفعله يمكن فعلُه في أيّ مسار، لكن لنجرّبه في مسارنا posts/ id.
[01:04] في هذا المكان، يمكن أن نمتلك هذا الاستدعاء الراجع onError الذي سيعمل كلّما حدث خطأ. وسنستقبل الخطأ هنا.
[01:13] وفيه يمكننا أن نفعل ربّما شيئًا ما مثل الإبلاغ عن الخطأ كما ذكرنا. في الوقت الحالي، فلنسجّله ببساطة إلى وحدة التحكّم (console). ولنتأكّد أنّ هذا يعمل. عذرًا.
[01:25] فلنتأكّد أنّ هذا يعمل بإلقاء نَظرة على وحدة التحكّم لدينا هنا، ومن المفترض أن نحصل على الرسالة التي نسجّلها هنا وهي error loading post، ثم نحصل على خطأنا. ولتخصيص مكوّن الخطأ (error component) على نحوٍ مشابه لمكوّن الانتظار
[01:39] فنحن لَدَينا أيضًا مكوّن خطأ كذلك. ففي هذا المكان يمكننا تمرير مكوّن الخطأ، لكنّنا هنا سنحصل على بعض الأشياء. ففي مكوّن الخطأ هذا
[01:52] نحصل بالطبع على الخطأ نفسه، ونحصل كذلك على دالة إعادة الضبط (reset). وفي الخطأ سنمتلك الخطأ نفسه، بالطبع رسالة الخطأ (error message) وما إلى ذلك. وهذه دالة reset نستطيع استدعاءها كي
[02:04] نُعيد المحاولة (retry) ونُعيد تصيير مسارنا. فلنقل أنّه هنا يمكننا أن نُرجع مكوّنًا.
[02:11] لنُرجع وسم div. وربّما نعرض رسالة الخطأ هكذا.
[02:18] وربّما ما بعد رسالة الخطأ، لنحفظ هذا ولنحدّث الصفحة. من المفترض أنّنا نحصل الآن على هذه الرسالة. ويمكن أن نمتلك زرًّا هنا ونستدعي دالة reset هذه كي — كي — نُعيد المحاولة ونُعيد تصيير المسار. فلنضِف
[02:33] زرًّا هنا. فلنقل ربّما retry.
[02:37] ثم عبر onClick يمكننا ببساطة استدعاء دالة reset لدينا هكذا.
[02:46] فالآن من المفترض أن نمتلك هذا الزر، وهذا الزر سيحاول إعادة تصيير مكوّن المسار (route component) أي هذا المكوّن هنا. لكنّه لن يحاول تحميل البيانات مجددًا؛ أي أنّ دالة التحميل (loader function) لن تعمل مرةً أخرى. و
[03:03] في الواقع، الخطأ في حالتنا يأتي من دالة التحميل هذه. إنّه يأتي عندما نجلب التدوينة. فربّما في بعض المواقف نودّ، عندما نُعيد المحاولة لإعادة تحميل المسار، أن نحاول جلب البيانات من جديد كذلك. فإذا فتحتَ
[03:17] شبكتَنا هنا وننقر — ننقر — على retry، فكما ترى لا تُعاد إرسال أيّ طلبات (requests).
[03:24] إذًا كي نُعيد المحاولة ونجلب البيانات، إضافةً إلى إعادة تصيير المسار، يمكننا استخدام router.invalidate بدلًا من ذلك. تذكّر أنّنا استخدمنا router.invalidate سابقًا لإعادة حساب سياقنا (context). ويمكننا استخدامه
[03:39] لهذا الغرض أيضًا. فلنُعرِّف موجّهًا (router) هنا. وللوصول إلى موجّهنا، يمكننا استخدام خطّاف (hook) useRouter. وبالطبع، يُستورَد useRouter من TanStack Router. والآن، بدلًا من reset، يمكننا ربّما استدعاء router.invalidate.
[03:57] والآن إذا حفظتَ هذا، فلنحدّث الصفحة أيضًا. وفلنُفرِّغ شبكتَنا هنا.
[04:04] ولننقر على retry. وكما ترى الآن، عندما نُعيد المحاولة تُعاد إرسالُ الطلبات.
[04:13] وبالطبع ما زلنا سنحصل على الخطأ لأنّنا نمرّر معرّفًا خاطئًا هنا. فلنقل الآن أنّه لا يوجد لدينا هذا الخطأ من الشبكة أو الـ API. فلنذهب إلى تدوينة موجودة فعلًا.
[04:25] الآن لا يوجد لدينا خطأ. كل شيء يعمل جيدًا. فلنقل أنّه كان لَدَينا خطأ آخر لم يحدث في دالة التحميل لدينا عندما نحمّل بياناتنا.
[04:33] لكنّه حدث عندما نصيّر المكوّن نفسه. ففي هذا المكان، في مكوّن التدوينة لدينا، لنقل أنّنا نحاول ربّما الوصول إلى شيءٍ لا يوجد. بالطبع، سيلتقط TypeScript ذلك. لكن لنقل أنّك
[04:45] واجهتَ هذا الخطأ عن طريق الخطأ، وسيظهر الآن مكوّن الخطأ لدينا مع خطأنا. لكن إن لاحظتَ في وحدة التحكّم، فلن نحصل على الاستدعاء الراجع onError هنا. لم يعمل هذا عندما
[05:02] كان لدينا خطأ في مكوّننا عند تصيير مكوّننا. وسيعمل هذا إذا كانت لديك أخطاء أثناء تحميل بياناتك. لكن إذا كانت لديك أخطاء في مكوّنك أثناء تصيير مكوّنك، فسنحصل عليها في
[05:14] onCatch. إذًا يمكن أن نمتلك onCatch هنا، وفي onCatch يمكننا أيضًا الإبلاغ عن هذا الخطأ. فسنحصل على الخطأ. فلنسجّل الخطأ إلى وحدة التحكّم كذلك. فلنقل هنا onCatch، وإذا حفظنا هذا و— عذرًا —
[05:36] حدّثنا الصفحة الآن، فمن المفترض أن يكون هذا onCatch يعمل، ويمكننا هنا أيضًا الإبلاغ عن الخطأ.
[05:45] فما يحدث في الكواليس هو أنّه عندما تصيّر مكوّنك أي مكوّن مسارَك، فسوف يغلّفه TanStack Router تلقائيًا بمكوّن هو CatchBoundary، وهذا المكوّن
[05:58] سيلتقط أيّ خطأ يحصل في أبنائه (children). فإذا حصل خطأٌ في الأبناء سيلتقطه هذا المكوّن، وعندما يحدث ذلك ستمتلك هذا الاستدعاء الراجع onCatch هنا يمكنك تمريره كي
[06:12] ترَى الخطأ وتسجّله وما إلى ذلك. غير أنّنا لسنا مضطرّين إلى فعل ذلك يدويًا؛ هذا ما يفعله TanStack Router. ويمكننا الوصول إلى دالة onCatch هذه هنا. عندما نُعرِّف مسارَنا