| المفهوم الهندسي | دوره في ريأكت | النتيجة البرمجية |
|---|---|---|
| map | المرور على كل عنصر داخل المصفوفة وتحويله إلى عنصر واجهة JSX | توليد قوائم تفاعلية ديناميكياً بناءً على حجم البيانات الفعلية القادمة من السيرفر |
| key | إعطاء رمز تعريفي فريد ومستقر لكل عنصر يتم تحويله داخل القائمة | تمكين خوارزمية Diffing من تتبع العناصر التي تغيرت أو تم حذفها بدقة دون إعادة رندرة القائمة بالكامل |
| id | الاعتماد على المعرف القادم من قاعدة البيانات كقيمة للـ key بدلاً من الـ index | منع حدوث أخطاء خفية في ترتيب القوائم وعرض البيانات أثناء الحذف والفلترة |
في بناء المواقع الحقيقية، لن تقوم بكتابة المكونات يدوياً واحداً تلو الآخر كما فعلنا سابقاً. بدلاً من ذلك، ستصلك البيانات من الخلفية (Backend) على شكل مصفوفة من الكائنات (Array of Objects).
في الويب التقليدي، كنت تضطر لكتابة حلقة تكرارية مثل for loop وتقوم بدفع الوسوم في الصفحة. ولكن في React، نظرًا لأننا نكتب JSX داخل أقواس مجعدة { } تدعم التعابير البرمجية فقط، فإن استخدام الـ for loop التقليدية ممنوع داخل الواجهة!
السبيل الهندسي الرسمي المعتمد في ريأكت لعرض المصفوفات هو دالة جافا سكريبت التحويلية map().
تأخذ دالة map() المصفوفة الأصيلة، وتمر على كل عنصر فيها واحداً تلو الآخر، وتطلب منك إرجاع وسم JSX لكل عنصر. في النهاية، تنتج دالة map() مصفوفة جديدة مليئة بعناصر الـ JSX التي تفهمها ريأكت وتقوم بطباعتها متتالية على الشاشة بنظافة وسرعة مذهلة.
بمجرد أن تقوم بتحويل مصفوفة إلى عناصر JSX باستخدام map()، ستلاحظ ظهور تحذير أصفر مزعج في الـ Console ينبهك إلى الملاحظة الشهيرة:
⚠️ تحذير
Warning: Each child in a list should have a unique "key" prop.
دعنا نرى كيف نأخذ مصفوفة من الكائنات تحتوي على بيانات كورسات منصة نوفيل، ونقوم بعرضها ديناميكياً مع الالتزام التام بقاعدة الـ key:
| 1 | function CourseList() { |
| 2 | // مصفوفة بيانات قادمة (تُمثل البيانات التي تصل من قاعدة البيانات) |
| 3 | const courses = [ |
| 4 | { id: 'c101', title: 'أساسيات JavaScript الحديثة', price: 25, category: 'JS' }, |
| 5 | { id: 'c102', title: 'احتراف React.js للمتقدمين', price: 50, category: 'React' }, |
| 6 | { id: 'c103', title: 'بناء سيرفرات خفيفة بـ Node.js', price: 40, category: 'Node' }, |
| 7 | { id: 'c104', title: 'تصميم الواجهات مع Tailwind CSS', price: 30, category: 'CSS' } |
| 8 | ]; |
| 9 | |
| 10 | return ( |
| 11 | <div className="courses-wrapper" style={{ padding: '20px' }}> |
| 12 | <h2>قائمة الكورسات المتاحة حالياً</h2> |
| 13 | |
| 14 | <div style={{ display: 'grid', gap: '15px' }}> |
| 15 | {/* استخدام دالة map للقيام بالمرور التحويلي على المصفوفة */} |
| 16 | {courses.map((course) => ( |
| 17 | // قاعدة صارمة: يجب وضع السمة key على الوسم الأب المباشر وإعطاؤه قيمة فريدة كالـ id |
| 18 | <div |
| 19 | key={course.id} |
| 20 | style={{ border: '1px solid #673ab7', padding: '15px', borderRadius: '8px' }} |
| 21 | > |
| 22 | <h3 style={{ margin: '0 0 10px 0', color: '#673ab7' }}>{course.title}</h3> |
| 23 | <p>التصنيف: {course.category}</p> |
| 24 | <p>السعر: <strong>{course.price} $</strong></p> |
| 25 | </div> |
| 26 | ))} |
| 27 | </div> |
| 28 | </div> |
| 29 | ); |
| 30 | } |
| 31 | |
| 32 | export default CourseList; |
⚠️ تحذير
إياك واستخدام ترتيب العنصر في المصفوفة (index) كقيمة للـ key
ℹ️ معلومة
يلجأ المبتدئون أحياناً لكتابة courses.map((course, index) => <div key={index}>) للهروب من تحذير ريأكت. هذا تصرف خاطئ خطير جداً!
لماذا؟ لأن الـ index هو رقم متغير يعتمد على موقع العنصر في الترتيب؛ لو قمت بحذف العنصر الأول من القائمة أو قمت بعمل إعادة ترتيب (Sorting)، سيتغير الـ index لكل العناصر المتبقية! ستظن ريأكت حينها أن العناصر قد تغيرت بياناتها بالكامل وتتداخل الحالات والتنسيقات المكتوبة بداخلها (تحدث مشكلة شهيرة مع حقول المدخلات inputs).