دورة TanStack Router الكاملة › دورة TanStack Router الكاملة 1 / 1 السابق التالي

حماية المسارات (Protecting Routes)

الدرس 32 من 34 · دورة TanStack Router الكاملة · 9 دقيقة و46 ثانية

الفيديو الأصلي باللغة الإنجليزية. إذا تعذّر تشغيله هنا، شاهده على موقع المؤلف. التفريغ النصي العربي الكامل أدناه.

مستودع الكود لهذا الدرس

الفرع: auth-1

[00:01] نحمي مساراتنا إذا كان لَدَينا نوعًا ما من نظام المصادقة (authentication). إذًا بالطبع، في الوقت الحالي لا نملك نظامَ مصادقةٍ حقيقيًّا. غير أنّ لَدَينا هذا الزرّ هنا حيث نملك بعض الحالة (state). إذًا تذكّر أنّه في ملفّ main.tsx لدينا،

[00:14] نملك حالة المستخدم (user) هذه التي نمرّرها هنا كسياق (context) إلى موفّر الموجّه (router provider). إذًا لنستخدم هذا. فلنفترض أنّ لَدَينا نظامَ مصادقةٍ حقيقيًّا حيث هذا المستخدم حقيقيٌّ فعلًا، وأنّنا نعتمد على هذا المستخدم كي

[00:29] يكُون موجودًا كي نعرف إن كنّا في وضع تسجيل الدخول (logged in) أم لا، وبناءً عليه سَنحمي بعض المسارات، وربّما في فيديو لاحق إذا أحببت يمكننا أن ندمج مصادقةً حقيقيةً فعليةً مع TanStack Router.

[00:42] إذًا كما نعرف الآن، لَدَينا شجرة مساراتنا (route tree) في TanStack Router. فلننشئ فعلًا، ربّما، واحدةً أخرى هنا. فلنقل أنّ لَدَينا مسارَ الجذر (root route) هنا.

[00:53] إذًا هذا هو جذرنا، ولنقل أنّ لَدَينا ربّما بعض المسارات العلنية (public). فلنقل أنّ هذه صفحة about أيضًا. ولنقل أنّ لَدَينا مسارًا آخر نحتاج إلى حمايته. فلنقل أنّ هذا محميّ.

[01:07] كما نعرف الآن، لكلّ مسار لَدَينا دالّة تحميل (loader function) سَتحمّل البيانات. غير أنّ لَدَينا أيضًا دالّة beforeLoad. دالّة beforeLoad هذه سَتُستدعى، كما نعرف، قبل تحميل أيّ بيانات لمسارنا وقبل

[01:23] تصيير المسار. ولذلك سَيَكُون هذا مكانًا مثاليًّا لفحص أمور المصادقة، لأنّ لَدَينا وصولًا إلى سياقنا هنا كذلك. يمكننا فحص سياقنا، وبناءً عليه يمكننا ربّما

[01:34] أن نُعيد التوجيه (redirect) أو أن نرمي خطأً (error) داخل beforeLoad، وبفعل ذلك لن يُصيَّر المسار. ولسنا فقط ذلك، بل يمكننا أيضًا حماية مجموعة من المسارات باستخدام التخطيطات (layouts). فلنقل أنّ لَدَينا ربّما مجموعةً من المسارات هنا و

[01:51] كلّ هذه تحتاج إلى الحماية، وبدلًا من اضطرارنا إلى إضافة دالّة beforeLoad لكلّ واحد منها وفحص المصادقة وهكذا، ما يمكننا فعله هو أن نضيف أصلًا (parent) هنا — تخطيطًا أصليًّا (parent layout) لكلّ هذه الدوالّ

[02:05] أو لكلّ هذه المسارات على هذا النحو. ومن هنا، في هذا التخطيط، يمكننا إضافة دالّة beforeLoad. وفي هذه الدالّة beforeLoad يمكننا فحص المصادقة. وإذا رمينا خطأً هنا فلن يحاول أيّ من الأبناء (children) أن

[02:18] يتحمّل. إذًا يمكننا استخدام التخطيطات لحماية مسارات متعدّدة كذلك. فلنرَ ذلك في محلّه فعلًا.

[02:27] إذًا أوّلًا سأضيف مسارًا جديدًا هنا لتسجيل الدخول (login). سأضيف مسار login. ملفّ جديد: login.tsx.

[02:36] ‏.tsx وسنضيف ببساطة الزرّ نفسه الموجود عندنا هنا. تذكّر أنّه في جذرنا كان لَدَينا زرّ تسجيل الدخول هذا.

[02:47] يمكننا فعل الشيء نفسه. لننسخ هذا فعلًا، وفي مسار login هذا سَنضع زرّ تسجيل دخول. وطريقة الوصول إلى login هي، تذكّر، استخدام السياق. بما أنّك تمرّر دالّة login هذه كـ

[03:02] سياق في جذرنا فسَتَكُون متاحةً في كلّ مكان.

[03:06] إذًا هنا يمكننا، من مسارنا — عذرًا — من route.useRouteContext، أن نجلب دالّة login لدينا هكذا. والآن بحفظ هذا يمكننا الانتقال إلى /login وسَيَكُون لَدَينا صفحةُ الدخول هذه.

[03:24] إذا نقرنا على login فينبغي أن نكُون مسجّلَي الدخول. بالطبع، عندما نسجل الدخول ينبغي أن نُعاد التوجيه إلى مكانٍ آخر، وأيضًا لا ينبغي أن نتمكّن من الوصول إلى مسار login هذا إذا كنّا في وضع تسجيل الدخول أصلًا.

[03:35] لكن فلنشغّل أنفسنا بذلك بعد قليل.

[03:37] أوّلًا، فلنجرّب ربّما حماية مسار استنادًا إلى سياقنا عن طريق إضافة دالّة beforeLoad للمسار الذي نحتاج إلى حمايته. فلنقل — لنفعل ذلك لمسار about مثلًا. فلنقل أنّ هذا محميّ إذًا. فلننتقل إلى

[03:52] about.tsx هنا. وما نحتاج إلى فعله هو إضافة دالّة beforeLoad. ستكون لَدَينا هنا وصولًا إلى سياقنا، صحيح؟ فداخل هنا يمكننا الحصول على سياقنا ويمكننا ببساطة أن نفحص إن كان لَدَينا

[04:07] مستخدم. إذًا إذا لم يكن context.user موجودًا. فلنفترض أنّنا خارج وضع تسجيل الدخول (logged out). ولكي نمنع البيانات من التحمّل والمسار من التصيير، نحتاج إمّا إلى رمي خطأ أو يمكننا أيضًا إجراء إعادة توجيه برمي redirect هنا.

[04:25] وredirect يمكننا استيرادها من TanStack React Router هكذا.

[04:30] ومن هنا يمكننا، بالطبع، تمرير المكان الذي نحتاج إلى إعادة التوجيه إليه. إذًا يمكننا تمرير to هنا ولنعيد التوجيه إلى مسار login مثلًا. إذًا الآن، بحفظ هذا ومحاولة الوصول إلى about سَنُعاد التوجيه إلى صفحة الدخول. الآن عندما ننقر

[04:49] على login، فلنقل أنّنا نحتاج إلى إعادة التوجيه إلى المسار الذي كنّا نحاول الوصول إليه. فلنقل أنّنا كنّا نحاول الوصول إلى — كما رأينا هنا — المسار المحميّ وهو /about. سَيُعاد توجيهنا إلى login لأنّ

[05:02] about محميّ. إذًا فلنقل أنّنا عند الدخول فعلًا نحتاج إلى الانتقال إلى الصفحة التي كنّا نحاول الوصول إليها وهي about. إذًا لكي ننجز ذلك علينا تمرير الصفحة التي كنّا نحاول الوصول إليها كمعامِل بحث (search parameter) هنا في مسار

[05:14] مسار login. فلنجري ذلك بإضافة، عند إعادة التوجيه هنا، معامِل بحث، ولنسمّه ربّما redirect، ثم نمرّر في هنا موقع مسار about الذي يمكننا الوصول إليه من هنا. يمكن أن يكُون لَدَينا وصول

[05:32] إلى location ويمكننا إعادة التوجيه إلى location.href.

[05:38] إذا حفظنا هذا الآن وحاولنا الوصول إلى about، فلنجرب معًا الوصول إلى about من هنا.

[05:45] الآن سَنُعاد توجيهنا إلى login. غير أنّ لَدَينا هذا المعامِلَ البحثيّ redirect وهو يشير إلى مسار about. الآن عند دخولنا نحتاج إلى جلب هذا إن وُجد وإعادة التوجيه إليه. وإلا فليكن لَدَينا ربّما مكانٌ افتراضيّ نُعيد

[05:59] التوجيه إليه. فلنقل — الصفحة الرئيسة أيضًا — يعني مسار home.

[06:02] إذًا في مسار login لدينا، فلنقل أنّه بعد تسجيل الدخول لنضع ربّما أقواسًا معقوفةً (curly brackets) هنا.

[06:12] وبعد تسجيل الدخول، فلنقل أنّنا نحتاج إلى التنقّل (navigate). إذًا لكي نفعل ذلك يمكننا استخدام خطّاف (hook) useNavigate. إذًا في مسارنا لَدَينا وصول إلى useNavigate.

[06:22] ومن هنا يمكننا أن ننجز تنقّلنا.

[06:26] غير أنّنا فلننسَ أيضًا — الآن بما أنّ لَدَينا معامِل بحث لهذا المسار — أنّنا نحتاج إلى تعريف مخطط (schema) له. إذًا سأضع هذا هنا وأستورد Zod. سَنمتلك redirect وهو اختياريّ (optional). ثم ينبغي علينا

[06:39] إضافة validateSearch هنا. استورد Zod validator (مُحقِّق Zod) إن كنت تستخدم Zod ومرّر له المخطط.

[06:48] والآن الآن — أخيرًا — من هنا، بعد تسجيل الدخول، فلنَنتقُل — فلنقف هنا أيضًا — ولنجلب المعامِلَ البحثيّ redirect الذي يمكننا جلبه من خطّافنا كذلك. إذًا من خطّاف useSearch لدينا. إذًا في مسارنا لَدَينا

[07:06] useSearch ومن هنا سَيَكُون لَدَينا وصول إلى redirect الذي — يمكن — يمكن أن يكون undefined. إذًا نحتاج هنا إمّا إلى إعادة التوجيه نحو هذا أو التراجع (fall back) إلى مسار home هكذا.

[07:20] إذًا الآن عند النقر على login ينبغي أن نُعاد التوجيه إلى about. الآن، ما دمنا مسجّلَي الدخول، يمكننا رؤية مسار about لدينا بشكل عادي. بالطبع إذا عاودنا التحديث (refresh) فبما أنّنا لا نُبقي هذه الحالة محفوظةً سَنخرج من وضع تسجيل الدخول. فلنحاكي الآن

[07:32] أنّنا مسجّلَو الدخول بدايةً بالتوجه هنا إلى جذرنا — في هذا الموضع — حين نمرّر سياقنا، سياقَ المستخدم (user context)، وكان — عذرًا — كان فعلًا في ملفّ main.tsx. من هنا، بدلًا من null، فلنمرّر أيّ

[07:50] مستخدمٍ شكليّ (dummy) ربّما هكذا.

[07:54] والآن عند التحديث ينبغي أن نكُون مسجّلَي الدخول وأن نتمكّن من زيارة مسار about. المشكلة الآن أنّه إذا انتقلنا إلى login سَنتمكّن من الانتقال إلى login رغم أنّنا مسجّلَو الدخول. إذًا نحتاج في مسار

[08:08] login لدينا أيضًا إلى إضافة دالّة beforeLoad وفحص لمعرفة هل كنّا في وضع تسجيل الدخول أصلًا. لا ينبغي أن نتمكّن من رؤية هذا المسار.

[08:16] إذًا من هنا يمكننا كذلك الحصول على وصول إلى سياقنا.

[08:22] ولنجلب أيضًا معامِلَ البحث لدينا كي نتمكّن من الانتقال إلى المعامِل البحثيّ redirect إذا كان لَدَينا.

[08:29] إذًا الآن يمكننا فحص context.user — إن كنّا في وضع تسجيل الدخول أصلًا فلنرمي أمر redirect وننتقل إلى search.redirect — عذرًا — أو إلى صفحتنا الرئيسة هكذا.

[08:49] أظنّ أنّ لي — سَهمًا (arrow) ناقصًا هنا.

[08:53] أجل. إذًا الآن إذا حاولنا الانتقال إلى login فسَنُعاد التوجيه إلى مسار home. لنقل الآن أنّنا في مسار about ونحن مسجّلَو الدخول، وبالطبع about محميّ كما فعلنا، لنقل أنّنا ننقر على الخروج (log out). الآن

[09:08] تذكّر أنّه عندما ننقر على log out — عذرًا — في main.tsx تذكّر أنّنا عند الخروج من التسجيل نستدعي router.invalidate وكما ذكرنا سيجبر router.invalidate أيّ مسار نشِط على إعادة التحمّل. إذًا عندما ننقر على log out الآن

[09:23] سَنُعاد توجيهنا لأنّ مسار about لدينا سَيعيد التحمّل ولَدَينا فحصَنا هنا الذي سَيعيد توجيهنا إلى login لأنّنا خارج وضع تسجيل الدخول الآن ولسنا نملك مستخدمًا في سياقنا.

[09:38] إذًا يبدو هذا جيدًا. لنمضِ في الخيار الآخر الّذي لَدَينا هنا وهو حماية مسارات متعدّدة باستخدام التخطيطات كذلك. إذًا، كما ذكرنا،