00:00:00توقف عن استخدام مكونات Radix وانتقل إلى البديل الأفضل: Base UI؛ خاصة إذا كنت من عشاق Shadcn.
00:00:06هناك بالفعل خيار للتحول الآن. إذا لم تسمع بـ Base UI من قبل، فهي في الواقع
00:00:10من المبتكرين الأصليين لـ Radix وFloating UI وMaterial UI، وهي مكتبة واجهة مستخدم بدون واجهة رسومية (Headless UI).
00:00:15فهي توفر الوظائف وسهولة الاستخدام للمكونات، لكنك لا تزال المتحكم في التصميم،
00:00:20وهو أمر بالغ الأهمية حالياً، لأن النماذج اللغوية الكبيرة (LLMs) لا تزال غير بارعة في معالجة الحالات الاستثنائية واحتياجات سهولة الوصول.
00:00:24لكن ما وصفته للتو هو بالضبط ما تفعله Radix أيضاً.
00:00:30فلماذا تحتاج إلى واحدة جديدة؟ دعنا ننتقل مباشرة لأوضح لك الفروق الجوهرية.
00:00:35سأبدأ سريعاً بعرض وثائق Base UI لترى جميع
00:00:44المكونات المتاحة. على اليسار، يمكنك رؤية أن لديهم تقريباً كل ما قد
00:00:48تحتاجه في مكتبة مكوناتك، وحتى بعض المكونات المتقدمة مثل “Combo Box” المتوفر هنا،
00:00:52والذي لا يمكنك الحصول عليه في Radix. وستلاحظ وجود أمثلة رائعة لكل هذه المكونات حول كيفية
00:00:57تنفيذها وتنسيقها باستخدام أشياء مثل CSS Modules، كما يمكنك تحويل المثال لاستخدام Tailwind
00:01:01إذا أردت أيضاً. التوثيق ممتاز حقاً، لكننا لسنا هنا لمقارنة
00:01:06التوثيق، لذا دعونا ننتقل مباشرة إلى الفرق الرئيسي الأول، وهو الذي
00:01:10ربما ستسمعه كثيراً، وهو أن Base UI تخضع لصيانة نشطة، بينما Radix في حالة شبه متوقفة.
00:01:15إذا ألقينا نظرة على مخططات مساهمات GitHub، ستجد أن Base UI
00:01:20تنمو باستمرار، بينما يبدو أن Radix لا تتلقى سوى تعديلات عابرة بين الحين والآخر، ولكن الأمر يصبح
00:01:25أوضح عند النظر إلى طلبات السحب (PRs) والمشكلات التي تم إغلاقها في الشهر الأخير. يمكنك
00:01:29رؤية أن Base UI أغلقت 58 مشكلة ودمجت 154 طلب سحب، بينما لم تغلق Radix أي مشكلة أو تدمج أي طلب سحب.
00:01:36خلاصة ما حدث لـ Radix هي أن شركة WorkOS اشترت الشركة المطورة لـ Radix
00:01:42لكنها لم تستثمر فيها حقاً، فغادر فريق Radix بمعظمه، وانتهى بنا المطاف إلى هذا الوضع.
00:01:47حتى أن المبتكر المشارك لـ Radix قال إنه لن يستخدمها إلا كملاذ أخير، وهو الآن
00:01:53يعمل على Base UI. لذا فالأمر يتعلق بضمان أن تطبيقاتك وتوابعها مدعومة
00:01:58بصيانة مستمرة، ولن تسبب لك صداعاً مستقبلياً إذا واجهت ثغرة لن يتم إصلاحها.
00:02:02والأفضل من ذلك هو أن Base UI صُممت لتكون مشابهة جداً لـ Radix، لذا لن تكون الهجرة إليها صعبة.
00:02:08لكن هذا لا يعني عدم وجود تحسينات إضافية، ومن تحسيناتي المفضلة
00:02:13هي كيفية تعاملهم مع خاصية “as child” الموجودة في Radix. إذا لم تكن
00:02:17قد شاهدت هذا من قبل، فلدي هنا مكون “select” من Radix، وإذا أردت عرض مكون مخصص
00:02:22هنا كمشغل للقائمة (select trigger)، فبمجرد لفه بمكون “select trigger”، ستلاحظ
00:02:27ظهور مكون مغلف (wrapper) ومعه الزر الذي يحمل نص “Subscribe” - وبالمناسبة، يجب عليك النقر عليه.
00:02:31لكن إذا أردت فعلياً جعل زر الاشتراك نفسه يعمل كمشغل للقائمة،
00:02:36فكل ما علي فعله في Radix هو إضافة خاصية “as child” إلى المشغل، وهذا يخبر
00:02:41Radix بدمج جميع الخصائص والوظائف الخاصة بمشغل القائمة مع المكون الذي
00:02:46وضعناه كعنصر تابع له. يمكنك رؤية ذلك من خلال دمج اسم الكلاس (class name) هنا
00:02:50مع الزر، وفي هذه الحالة يتم استبداله. فإذا حذفت تلك الخاصية وحفظت،
00:02:55سترى أن لدينا الآن زراً يعمل وظيفياً كمشغل للقائمة الخاصة بي.
00:03:00هذه وظيفة مفيدة جداً، لكن الشكوى الشائعة كانت أنها لم تكن صريحة بما يكفي.
00:03:04وأعترف أنني أحياناً كنت أتغاضى عن هذه الخاصية عند مراجعة الكود بسرعة، وبالتالي كنت أخطئ في تحديد سبب المشكلة.
00:03:09لذا إذا انتقلنا لرؤية كيفية تنفيذ ذلك في Base UI، ستجد أنك تحصل على نفس الوظيفة تماماً.
00:03:13لدي زر هنا يعمل كمشغل للقائمة، ولكن إذا نظرت إلى الكود،
00:03:18ستجد أن Base UI تستخدم خاصية “render” بدلاً من “as child”.
00:03:24وفي خاصية “render”، تحدد المكون الذي تريد عرضه كمشغل للقائمة.
00:03:29هذا تغيير طفيف، لكني أرى أنه يجعل الأمر أكثر وضوحاً
00:03:34بشأن ما يحدث بالضبط؛ فهو يعرض هذا المكون حرفياً، فلا داعي للبحث
00:03:39عما إذا كان يستخدم المكون التابع أو البحث عن خاصية “as child”. كما قلت،
00:03:43هو تغيير بسيط ولكنه ممتاز في رأيي. وهذه ليست حتى القوة الكاملة لخاصية “render”.
00:03:48يمكننا أن نرى هنا عند بناء مفتاح تبديل (switch) أنه يمكننا تمرير دالة إلى
00:03:52خاصية “render”؛ مما يتيح لنا الوصول إلى خصائص حالة المكون الذي
00:03:56نقوم ببنائه، مثل “switch thumb” في هذه الحالة. ما يتيحه لنا هذا هو اختيار المكون
00:04:01الذي نريد تطبيق الخصائص الممررة عليه، ومن ثم يمكننا أيضاً إجراء منطق عرض مخصص
00:04:05أو منطق تنسيق مخصص بناءً على حالة المكون. ففي حالة
00:04:10مفتاح التبديل، يمكننا التحقق مما إذا كان مفعلاً أو معدلاً أو معطلاً أو ممتلئاً وغيرها الكثير.
00:04:14في هذه الحالة، نقوم ببساطة بالتحقق مما إذا كان مفعلاً أم لا، وبناءً على ذلك
00:04:18نعرض أيقونة مختلفة. هناك أيضاً “hook” يتيح لك دمج خاصية العرض (render prop)
00:04:22في مكوناتك المخصصة، لكننا ندخل هنا في تفاصيل متقدمة نوعاً ما. آمل
00:04:27أن تكون قد أدركت أن أي شيء مخصص تريد القيام به، يمكنك التعامل معه في Base UI.
00:04:31والآن بالعودة إلى مكون القائمة (select)، الفرق التالي هو أن Base UI تسمح لبعض
00:04:35المكونات بأن تكون مدفوعة بالبيانات (data-driven). نرى هذا في حالة مكون القائمة هنا؛
00:04:40ما لدي حالياً هو كود Radix select؛ لدينا مصفوفة تحتوي على الاسم والقيمة،
00:04:44ولإدراج القيم فعلياً في القائمة بالأسفل، كل ما نحتاجه هو عمل “map” على مصفوفة “apples”
00:04:49ثم عرض “select item” في Radix، وهذا سيضيف القيم بهذا الشكل.
00:04:54أما إذا انتقلنا لكيفية القيام بذلك في Base UI، ستجد الأمر مشابهاً جداً. لا نزال نملك
00:04:59المصفوفة بالأعلى مع الأسماء والقيم، وبالأسفل لا نزال نقوم بعمل “map” وعرض
00:05:03“select item”، ولكننا ندرجها أيضاً في مكان آخر، وهو “select root” هنا.
00:05:08نحن ندرج تلك المصفوفة، وهذا له تأثير طفيف وذكي يعني أن المكون أصبح الآن
00:05:13على دراية بالبيانات التي سيعرضها قبل العرض الفعلي، مما يعني وجود
00:05:17أداء أفضل قليلاً، خاصة عندما يتعلق الأمر بالعرض من جانب الخادم (SSR).
00:05:22لكن هناك شيء واحد أعتقد أنه يمكن تحسينه في هذا الصدد؛ حالياً أقوم بتمرير “apples” كخاصية “items”،
00:05:26وفي الأسفل أقوم أيضاً بعمل “map” على نفس المصفوفة، فنحن نستخدمها في مكانين.
00:05:32ما أعتقد أن عليهم فعله هو ما تفعله مكتبة React Aria، وهي مكتبة أخرى بدون واجهة رسومية. هنا
00:05:36يمكنك رؤية مصفوفة الحيوانات لدينا؛ نمررها هنا كـ “items”، ومن ثم
00:05:41بالنسبة للعناصر التابعة، كل ما نفعله هو استخدام دالة، وهذه الدالة ستعرف كل العناصر التي
00:05:45تم تمريرها في العنصر الأب؛ وبذلك لا نستخدم المصفوفة في مكانين، حيث يعمل
00:05:50العنصر الأب كمزود للبيانات. لذا فمن المؤكد أنه شيء لا يزال من الممكن تحسينه قليلاً.
00:05:55ولكن بالعودة إلى مكون القائمة، أريد أن أريك فرقاً آخر،
00:05:59ولكن هذا الفرق خاص بمكون القائمة تحديداً، على عكس خاصية “render” والنهج
00:06:03المدفوع بالبيانات اللذين ستجدهما في أغلب المكونات. هذا الفرق خاص بـ “select”
00:06:08وهو إمكانية الاختيار المتعدد (multi-select)، وهو أمر كان مفقوداً بشكل مؤلم في Radix.
00:06:13يمكنك أن ترى هنا في Base UI أن كل ما عليك فعله هو إضافة خاصية “multiple” إلى “select root”
00:06:17سواء كانت قيمتها true أو false، لتصبح القائمة ببساطة تدعم الاختيار المتعدد، فالأمر بهذه السهولة.
00:06:22ولنذهب أبعد من ذلك، تمتلك Base UI مكونات أخرى نفتقدها في Radix مثل “Combo Box”
00:06:27و”Auto Complete”، وهذه هي ميزة الصيانة النشطة التي تسمح بالاستجابة لطلبات المستخدمين.
00:06:33والآن هناك فرقان آخران أريد إطلاعكم عليهما بين Radix وBase UI،
00:06:38وسننتقل لاستخدام مكون “checkbox” لأنني مللت قليلاً من
00:06:41مكون القائمة ذاك. الفرق الأول الذي أريد توضيحه يتعلق بالتنسيق (styling)؛
00:06:45توفر Base UI خياراً آخر للتنسيق أراه رائعاً حقاً. يمكننا أن نرى هنا حالياً
00:06:50أنني أستخدم Tailwind. يمكنك استخدام جميع الطرق التقليدية مثل CSS العادي أو CSS Modules
00:06:55وغيرها الكثير، ولكن إذا كنت تستخدم شيئاً مثل Tailwind، فإن إحدى الطرق
00:06:59المعتادة لتنسيق مكون كهذا هي استخدام خصائص البيانات (data attributes) للحالة، فلدينا هنا “data-checked”،
00:07:04وإذا كان مفعلاً، فسنستخدم لون الخلفية الأساسي. تلاحظون أن لدينا سلسلة نصية طويلة جداً
00:07:08للتنسيق في Tailwind، وهو ما قد يصبح مشكلة أحياناً. لذا
00:07:13أحد الخيارات التي توفرها Base UI للمساعدة في ذلك هو إمكانية استخدام دالة
00:07:17كاسم للكلاس (className). هذا يمنحك وصولاً إلى حالة المكون؛ ففي حالة
00:07:22صندوق الاختيار هنا، أقوم بتطبيق الأنماط بناءً على ما إذا كانت الحالة مفعّلة أو معطلة.
00:07:26تلاحظون أنني أقوم بذلك عبر شرط بسيط، وأرى أحياناً أن هذه الطريقة أسهل للملاحظة السريعة
00:07:31عند مراجعة الكود لمعرفة مصدر أنماط التفعيل والتعطيل بالضبط،
00:07:35بدلاً من فحص سطر واحد طويل والبحث عن خصائص البيانات تلك. أعتقد
00:07:40أنها تكون أكثر فائدة إذا كنت تستخدم شيئاً مثل Vanilla CSS، كما أنها تعمل
00:07:45بشكل رائع مع مكتبة أخرى أحبها تسمى Tailwind Variants. يمكنكم رؤية
00:07:49أنني أقوم ببساطة بتمرير الحالة إلى دالة checkbox الخاصة بي، وفي الأعلى في متغير checkbox الخاص بـ Tailwind
00:07:53لدينا الأنماط الأساسية، ولكن لدينا أيضاً المتغيرات (variants). ويمكنكم رؤية أنني أقول ببساطة:
00:07:58إذا كان “checked” صحيحاً فطبق هذه الأنماط، وإذا كان “disabled” صحيحاً فطبق هذه الأنماط.
00:08:02وأعتقد أن هذا أحياناً يكون أوضح بكثير من استخدام خصائص البيانات، لكن الأمر يعود لرأيك
00:08:06وما ترتاح لاستخدامه. من الرائع حقاً أن توفر لنا Base UI كل هذه الخيارات. سأضيف أيضاً
00:08:11أن استخدام دالة كاسم للكلاس كان شيئاً أعجبني لأول مرة في مكتبة React Aria.
00:08:14لذا يبدو لي أن لدينا React Aria من جهة، والتي تتطلب منحنى تعلم حاداً،
00:08:19وRadix من جهة أخرى وهي بسيطة للغاية، بينما جاءت Base UI لتقف في المنتصف وتأخذ
00:08:23الميزات التي أحببتها في كلتيهما لتقدم لي المكتبة المثالية. كانت هذه جميع
00:08:28الفروق الجوهرية التي أردت تغطيتها في هذا الفيديو، ولكن لا يزال هناك الكثير؛ فـ Base UI توفر
00:08:33دعماً ممتازاً لـ React Hook Form وTanStack Form، وتدعم الرسوم المتحركة لجعل
00:08:38عملية تحريك مكوناتك سهلة وسلسة، بل وتضم ميزات مثل “input scrubbing”
00:08:42والحوارات المتداخلة (nested dialogues) وتفعيل القوائم عند التمرير بالماوس. وأنا متأكد أنهم سيستجيبون
00:08:47لأي طلب منطقي على GitHub لأن المكتبة نشطة. ومع ذلك، تجدر الإشارة إلى
00:08:52أنني ربما لن أتسرع في تحويل تطبيقي المبني على Radix إلى Base UI فوراً، فلا أعتقد
00:08:57أن Radix معطلة تماماً. لكن إذا كنت أبدأ مشروعاً جديداً، سأستخدم Base UI بالتأكيد الآن،
00:09:02وإذا كنت أحتاج لميزة مثل Combo Box أو Auto-complete، سأفكر أيضاً في تحويل تطبيقي. أخبروني
00:09:07برأيكم في Base UI في التعليقات، ولا تنسوا الاشتراك، وكما هو الحال دائماً
00:09:11أراكم في الفيديو القادم.