مقاطع المسار الديناميكية (Dynamic Path Segments)
الدرس 10 من 34 · دورة TanStack Router الكاملة · 9 دقيقة و47 ثانية
الفيديو الأصلي باللغة الإنجليزية. إذا تعذّر تشغيله هنا، شاهده على موقع المؤلف. التفريغ النصي العربي الكامل أدناه.
مستودع شيفرة هذا الدرس
الفرع:
dynamic-path-segments
[00:01] طبعًا في حالات كثيرة في تطبيقاتك ستحتاج إلى وجود مسارات ذات معاملات ديناميكية (dynamic parameters). فقد يكون لدينا مثلًا مدوّنة أو ما شابه ذلك، فينبغي أن يكون عنوان URL لدينا ربما على الصيغة posts/ ثم معرّفٍ ما، وهذه المعرّف معامل ديناميكي.
[00:16] وهو أمر لا نعرفه مسبقًا. ففي هذه الحالة نستخدم كذلك صياغة خاصة عند تسمية ملفاتنا ومجلداتنا. وفي بقية هذا الفيديو سأتابع على الأرجح العمل بهذه الطريقة المسطّحة، لكن بالطبع، على النحو الذي تعرفه،
[00:31] فأي شيء يمكننا إنجازه بهذه الطريقة المسطّحة يمكن إنجازه بالمجلدات كذلك.
[00:35] حسنًا. فلنُنشئ إذن — لنقل — بعض المسارات التي نحتاج إليها. لنقل أن لدينا المسار الجذري هنا وأننا في حاجة إلى إنشاء ثلاثة مسارات جديدة: أحدُها مخصَّص للمنشورات posts وسيكون قائمةً بالمنشورات. فسنضعه هنا posts، ثم
[00:53] نحتاج إلى أن يكون لدينا posts/ ID. لكن ضع في حسبانك أن posts/ ID ينبغي أن يكون مسارًا مستقلًا لا ابنًا للـ posts.
[01:02] فسيبدو الأمر على هذا النحو: posts/ ID.
[01:05] فلنقل إذن أن هذا هو posts، وهذا هو posts/ id. ثم لنقل أننا نحتاج إلى مسار لتحرير منشور، فيلزمنا أن يكون عنوان URL لدينا على الصيغة posts/ id/edit. لكن ضع في حسبانك أيضًا أنه عند إنشاء مسار التحرير هذا فلا حاجة إلى أن يكون
[01:21] ابنًا للـ posts/ id؛ فلا نريده أن يكون هناك، بل نريده أن يكون هو الآخر مسارًا مستقلًا بذاته على الصيغة posts/ id/edit، لأنه سيحتوي على أشياء مختلفة تمامًا، فلن يكون ابنًا للـ posts/ id، وبالتالي فلن
[01:37] يكون لدينا هذا المسار منضمًّا إلى أي أب. فهل نتفق؟ فلنرَ إذن كيف يمكننا فعل ذلك. أنت تعرف مسبقًا كل ما يلزمنا لإنشاء ذلك، ما عدا بالطبع كيفية إنشاء المسارات الديناميكية التي سنراها الآن. فلنُنشئ أولًا مسار
[01:52] posts الذي سيحتوي ربما على قائمة بالمنشورات. وتذكّر أننا نريده ألّا يكون أبًا لأي شيء. ولفعل ذلك نكتفي بإنشاء ملف جديد وتسميته posts.index.tsx.
[02:06] فبإضافة index هذه لن يكون posts أبًا لأي شيء، إذ لن يتطابق هذا المسار إلا إذا كان عنوان URL لدينا posts تمامًا لا posts/ ثم شيئًا ما. وهذا ما نعرفه مسبقًا، صحيح؟ فأصبح لدينا الآن هذا المسار، وبالطبع يكون لدينا افتراضيًا هنا بعض
[02:21] النصوص المولَّدة هنا، أي hello posts.
[02:25] والآن لنقل أننا نحتاج إلى إنشاء posts/ id. ولكي ننشئ مسارًا ديناميكيًا نستخدم علامة الدولار، فنُنشئ ملفًا جديدًا، ثم لنقل posts، ولكي يكون لدينا معامل ديناميكي نستخدم علامة الدولار ونمنحه أي اسم نريده. فلنقل
[02:40] post ID أو ما شابه، ثم بصيغة tsx. وبالطبع إن كنت تستخدم طريقة المجلدات فيمكنك إنشاء مجلد اسمه posts وداخله ملف اسمه $postId.tsx.
[02:53] حسنًا. فيصبح بوسعنا الآن زيارة posts/ ثم شيئًا ما وسنصل إلى هذا المسار.
[03:01] والآن لنُنشئ أخيرًا المسار الأخير.
[03:03] فكما ذكرنا، يلزمنا أن يكون لدينا posts/ id/edit، صحيح؟ فيمكننا ببساطة القدوم إلى هنا وإنشاء ملف جديد: posts ثم نقطة ثم $postId ثم edit.tsx.
[03:21] غير أنه إذا نظرنا إلى أدوات المطوّر (dev tools) فسنرى أن مسار التحرير هذا سيكون ابنًا للـ posts/ ID. وكما ذكرنا نحتاج إلى أن تكون هذه المسارات مستقلة. فتذكّر كيف يُحقَّق ذلك، أي كيف نجعل edit لا
[03:37] يستخدم posts/$postId كأب. يمكننا فعل ذلك بطرائق عدة في الواقع: يمكننا إعادة تسمية هذا ليصبح posts.$postId.index على هذا النحو، فيصبح الآن مسارًا مستقلًا بذاته. وإذا نظرنا فسنكون
[03:53] لدينا ثلاثة مسارات مستقلة.
[03:55] posts، ثم post ID، ثم post ID/edit.
[04:01] لكن لدينا كذلك طريقة أخرى، فلنُعِد هذا كما كان من دون index.
[04:07] لدينا طريقة أخرى تستخدم تلك الصياغة بالشرطة السفلية. فتذكّر أننا، ولكي نجعل مسارًا ابنًا لا يستخدم أبَه كتخطيط (layout)، يمكننا أن نستخدم الشرطة السفلية، صحيح؟ فالآن في مسار التحرير هذا يمكننا أن نجعل هذا المسار لا يستخدم posts كَ
[04:23] أب، بإعادة تسمية هذا وإضافة الشرطة السفلية هنا بعد posts. والآن كما ترى نحصل على النتيجة نفسها: posts، ثم posts/$postId/edit، ثم posts/ id، صحيح؟ وهكذا صار لدينا هذان الأسلوبان في فعل ذلك. والآن بالطبع
[04:44] لنقل أننا نزور منشورًا ما.
[04:47] أي posts/ ثم معرّف ما في مسارنا. وبالطبع سنحتاج إلى استخدام هذا المعرّف بطريقة ما، وسنناقش لاحقًا دوال التحميل (loaders)، وهو على الأرجح الموضع الذي ستحتاج فيه إلى الوصول
[05:01] إلى هذا المعرّف، لأنك ستقوم هناك بتحميل بياناتك. غير أنه إن احتجت إلى الوصول إلى هذا المعرّف في مكوِّنك أيضًا، فبوسعك أن تقدم إلى هنا — إلى posts/$postId أو إلى مسار التحرير كذلك — ونفعل شيئًا ما، فلدينا
[05:16] هذا المسار، وفي هذا المسار لدينا بضعة خطاطيف (hooks) يمكننا الوصول إليها. فيمكننا إنشاء ثابت جديد هنا ثم نستدعي route.useParams — أعذرني — useParams، وهنا سنكون
[05:32] بوسعنا الوصول إلى post ID، وسيتوفر لنا هنا الإكمال التلقائي وسيكون كل شيء آمن الأنواع (type-safe) وما إلى ذلك. فأصبح لدينا post ID هنا، فلنضعه ربما في سطر جديد: post ID ونضعه هنا، وهكذا صار post ID لدينا.
[05:48] فلنفترض أن هذا المكوِّن (component) يعيش ربما في ملف مختلف، فلن نكون بإمكاننا الوصول إلى هذا المسار. فكيف يمكننا في هذه الحالة الوصول إلى useParams؟
[05:57] فلنفترض مثلاً أنه في مجلد components لدينا مكوِّن للمنشور، لنسمه post.tsx.
[06:05] ولننسخ في الواقع أشياءنا من هنا؛ فلننسخ هذا ولنُنشئ مكوِّنًا جديدًا ونضع هذا فيه فحسب.
[06:17] والآن نحتاج إلى الوصول إلى post ID في مكوِّن منفصل ثم استخدام هذا المكوِّن هنا. فلنُرجِع المكوِّن الذي عرَّفناه على هذا النحو. وبالطبع يمكننا تمرير post ID ربما كخاصية (prop) هنا، غير أن هناك
[06:33] طريقة أسهل لفعل ذلك. فلنحذف هذا.
[06:35] قد تظن أننا يمكننا ربما استيراد route، أي هذا المسار، من هذا الملف إلى هذا الملف ثم استخدام الخطِّاف. غير أن هذه ليست الطريقة الموصى بها لفعل ذلك؛ فالطريقة الموصى بها هي استخدام
[06:48] دالة اسمها getRouteApi. فلنُنشئ ثابتًا هنا ويمكننا استدعاء دالة getRouteApi، ويمكننا أن نمرِّر إلى هذه الدالة المسار الذي نحتاج إلى الحصول على واجهته (API) بخطاطيفه، أي خطاطيف هذا المسار. وفي حالتنا نحتاج إلى
[07:04] هذا المسار posts/$postId، وسنحصل من ذلك على كائن يتضمن خطاطيف هذا المسار، فيمكننا الوصول إلى useParams على هذا النحو.
[07:15] وبعدها يمكننا ببساطة الحصول من useParams على post ID. وكما ترى فكل شيء آمن الأنواع، ويتوفر لنا إكمال تلقائي لكل شيء. وكما ترى، سيظل هذا
[07:30] يعمل كذلك.
[07:33] حسنًا. أخيرًا، لنرَ كيف يمكننا الربط إلى المعاملات ذات المقاطع الديناميكية (dynamic segments)؛ فلنذهب ربما إلى /posts وسيتوفر لدينا لاحقًا قائمة المنشورات هنا، لكن لنجرِ الآن إضافة بعض الروابط الوهمية فحسب. فلنذهب إلى
[07:47] هذا المسار وهو مسار فهرس المنشورات (posts index).
[07:50] ولنقل أننا نحتاج إلى وضع روابط لبعض المنشورات هنا. بالطبع ما نزال نستخدم مكوِّن Link نفسه، ولنقل — مثلًا — go to post أو ما شابه. غير أنه عند تحديد معاملاتنا الديناميكية نفعل ذلك بطريقة معينة، إذ
[08:06] لا نضع في الخاصية to الـ posts مع المعرِّف نفسه، بل نضع posts/ ثم $postId، وهو ما يوافق مسار مجلدنا. ولكي نحدِّد هذا المعامل الديناميكي بصورة آمنة الأنواع نمرِّر له هنا في خاصية منفصلة
[08:24] وهي params. وستوفر لنا params أمان الأنواع، فإذا كتبنا حرف P هنا حصلنا على post ID، وإذا مرِّرناه ربما كرقم فسنحصل على خطأ؛ إذ يجب تمريره كسلسلة نصية، وإذا وقعنا ربما في خطأ مطبعي
[08:38] في اسم معاملنا فسنحصل على خطأ كذلك، وهكذا دواليك. وبهذا سنحصل على هذا الرابط go to post، وإذا نقرنا عليه فيجب أن يقودنا إلى هذا المنشور.
[08:51] ولدينا طريقة أخرى، فلنعد إلى الوراء ولنُنشئ رابطًا آخر.
[08:55] أو بل لنكرر هذا الرابط في الواقع. وبدلًا من تمرير المعاملات ككائن، يمكننا تمريرها كدالة كذلك، فنستطيع هنا أن نجلب معاملاتنا السابقة ثم
[09:09] معاملاتنا الجديدة.
[09:13] فقد نحتاج إلى المحافظة على شيء — لنقل أن لدينا معاملات ديناميكية عدة، أي معامل 1/ معامل 2/ معامل 3 وهكذا دواليك، ولم نكن بحاجة إلى تحديث واحد من هذه المعاملات فقط والحفاظ على البقية، فسيكون هذا مثاليًا
[09:28] في مثل هذا الموقف. وفي حالتنا، بالطبع، لن يحدث هذا أي فرق ذلك أن لدينا معاملًا ديناميكيًا واحدًا فحسب، أما إن كانت لدينا عدة معاملات
[09:38] فعندئذٍ بوسعنا الإبقاء على السابقة منها وتحديث واحدة. وفي حالتنا لدينا post ID وبوسعنا تمريره هنا كذلك، فسيكون بوسع هذا الرابط أن يعمل كذلك. وبالنظر إلى ذلك، الآن…