بناء بطاقة مرنة باستخدام Composition
في الدرس السابق استخدمنا Props لعرض بيانات مختلفة في بطاقتي التقدم. لدينا الآن بطاقة للدورات فيها عنوان و قائمة. نريد بطاقة ثانية لها الغلاف نفسه تقريبا، لكن تحت عنوان Next step نريد نصا و رابطا بدلا من القائمة.
كيف نجعل مكونا واحدا يدعم محتوى البطاقتين؟
هل نضيف Prop لكل نوع من المحتوى؟
قد نبدأ بتمرير كل تفاصيل البطاقة عبر Props:
<DashboardCard
title="Next step"
text="Review your React progress before the next lesson."
actionText="Review progress"
actionHref="#react-progress"
/>
<DashboardCard
title="My courses"
items={["React Foundations", "Git on Your Machine"]}
/>سيحتاج DashboardCard عندها إلى معرفة طريقة عرض قائمة أو نص و رابط. و كل نوع جديد من المحتوى سيضيف إليه Props و شروطا أخرى. لكن البطاقة لا تحتاج إلى كل هذه التفاصيل. هي تملك الغلاف و موضع العنوان. أما ما بعد العنوان فيحدده المكون الذي يستخدمها.
ضع المحتوى داخل البطاقة
يمكننا كتابة المحتوى المختلف بين وسمي كل بطاقة:
<DashboardCard title="My courses">
<ul>
<li>React Foundations</li>
<li>Git on Your Machine</li>
</ul>
</DashboardCard>
<DashboardCard title="Next step">
<p>Review your React progress before the next lesson.</p>
<a href="#react-progress">Review progress</a>
</DashboardCard>توقع أين سيظهر النص و الرابط. هل يمكن أن يظهرا داخل بطاقة My courses؟
يمرر React ما بين وسمي كل بطاقة إلى DashboardCard عبر Prop اسمها children. يعرضها المكون تحت العنوان:
export function DashboardCard({ title, children }) {
return (
<section className="dashboard-card" aria-label={title}>
<h2>{title}</h2>
{children}
</section>
);
}كل استخدام لـ DashboardCard يحصل على children الخاصة به. لا ينتقل محتوى بطاقة إلى الأخرى. و children ليست حالة مشتركة بينهما.
لماذا بقي title كـ Prop مسماة؟
يمكن وضع العنوان داخل البطاقة أيضا، لكننا نريد أن تعرض كل بطاقة عنوانها في الموضع نفسه. يستخدم DashboardCard العنوان كذلك في aria-label. لهذا يبقى title قيمة مسماة يعرف المكون أين يستخدمها، بينما يترك ما تحت العنوان مفتوحا لمحتوى يختاره الأب.
نسمي هذا Composition. يحدد المكون الغلاف الثابت و يضع الأب المحتوى المتغير داخله. لا يعني ذلك أن children أفضل دائما من Props المسماة؛ لكل منهما دور في هذه البطاقة.
جرب في المحرر
انظر إلى CourseList في dashboard-parts.jsx. من يملك <section> و <h2> بعد استخراج الغلاف؟ و ما الذي سيصبح children؟ توقع ذلك قبل التعديل.
أضف DashboardCard إلى الملف نفسه و استخدمه داخل CourseList مع إبقاء عناصر القائمة. استورد DashboardCard في index.jsx ثم أضف بطاقة Next step بعد CourseList. اجعل رابط Review progress يشير إلى #react-progress حتى يصل إلى بطاقة تقدم React. أبق بطاقتي التقدم كما هما.
أنشئ DashboardCard و استخدمه للبطاقتين.
في styles.css استبدل .course-list بـ .dashboard-card في قاعدة إطار البطاقة. أضف لونا واضحا للرابط و إطارا يظهر عند التركيز عليه بلوحة المفاتيح:
.dashboard-card a {
color: #1d4ed8;
}
.dashboard-card a:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 3px;
}استبدل .course-list بـ .dashboard-card و أضف تنسيق الرابط.
قبل فتح المعاينة توقع ترتيب العناصر داخل main. افتح المعاينة و جرب رابط Review progress ثم شغل الاختبارات.
اختبارات الكود لا تفحص CSS. راجع التنسيق في المعاينة: يجب أن يظهر إطار حول بطاقتي DashboardCard و لون واضح للرابط. انتقل إلى الرابط بمفتاح Tab و تأكد من ظهور إطار التركيز.
أظهر ترتيب البطاقات المتوقع
تظهر بطاقتا التقدم أولا. بعدهما قائمة الدورات ثم بطاقة Next step.
لماذا أبقينا title Prop مسماة و لم نضف Props مثل items و actionText؟ حاول الإجابة ثم افتح التفسير.
أظهر سبب الاختيار
العنوان جزء ثابت من تصميم البطاقة و يظهر في الموضع نفسه. أما المنطقة تحته فتقبل أشكالا مختلفة من JSX، لذلك يتركها DashboardCard للأب عبر children.
غلاف ثابت و محتوى يختاره الأب
صار DashboardCard يعرف موضع العنوان و المنطقة التي يعرض فيها {children}. لا يحتاج إلى معرفة هل أرسل الأب قائمة أو نصا و رابطا. هكذا نعيد استخدام الغلاف نفسه من دون نسخ JSX الخاص به أو إضافة Prop جديدة لكل شكل من المحتوى.
بهذا نكون قد أكملنا أول محور من الدورة. انتقلنا من تحديث الواجهة بأوامر DOM مباشرة إلى وصفها باستخدام JSX و المكونات و Props و Composition. في المحور القادم سنرى كيف تحدد البيانات ما يظهر في الواجهة، مثل اختيار حالة معينة أو بناء قائمة من عناصر مصفوفة.