غالباً ستريد عرض عدة مكونات متشابهة من مجموعة بيانات. يمكنك ان تستخدم دوال المصفوفات الخاصة بلغة JavaScript لكي تضاعف مصفوفة من البيانات. في هذه الصفحة، سوف تستخدم الدالتين الآتيتين: filter() و map() مع مكتبة React لتصفّي و تحول مصفوفتك المكونة من مجموعة بيانات إلى مصفوفة من المكونات.

You will learn

  • كيف يمكنك تصيير مكونات من مصوفة باستخدام دالة JavaScript map().
  • كيف يمكنك تصيير مكونات محددة فقط باستخدام دالة JavaScript filter().
  • متى ولماذا تستخدم مفاتيح React.

تصيير البيانات من مصفوفات.

افترض بأنَّ لديك قائمة بالمحتوى.

<ul>
<li>Creola Katherine Johnson: mathematician</li>
<li>Mario José Molina-Pasquel Henríquez: chemist</li>
<li>Mohammad Abdus Salam: physicist</li>
<li>Percy Lavon Julian: chemist</li>
<li>Subrahmanyan Chandrasekhar: astrophysicist</li>
</ul>

الاختلاف الوحيد بين تلك القوائم هو محتوياتها، أو بالأحرى بياناتها. غالباً ستحتاج إظهار نماذج من المكون نفسه باستخدام بيانات مختلفة عندما تبني الواجهات: ابتداءً من قوائم من التعليقات إلى معارض من صور الملفات الشخصية. في هذه الحالات، يمكنك تخزين تلك البيانات في كائنات و مصفوفات JavaScript واستخدام دالات مثل map() and filter() لتصيير قوائم من البيانات من تلك المصفوفات والكائنات.

هذا مثال صغير حول كيفية توليد قائمة عناصر من مصفوفة:

  1. انقل البيانات إلى مصفوفة:
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
  1. استخدم الدالة Map على أفراد القائمة people وحولها إلى مصفوفة من JSX عقد, listItems:
const listItems = people.map(person => <li>{person}</li>);
  1. قم بأرجاع القائمة listItems من مكونك مغلفة بوسم <ul>:
return <ul>{listItems}</ul>;

هذه هي النتيجة:

const people = [
  'Creola Katherine Johnson: mathematician',
  'Mario José Molina-Pasquel Henríquez: chemist',
  'Mohammad Abdus Salam: physicist',
  'Percy Lavon Julian: chemist',
  'Subrahmanyan Chandrasekhar: astrophysicist'
];

export default function List() {
  const listItems = people.map(person =>
    <li>{person}</li>
  );
  return <ul>{listItems}</ul>;
}

لاحظ sandbox في الأعلى يظهر خطأً في console:

Console
Warning: Each child in a list should have a unique “key” prop.

سوف تتعلم كيف تصلح هذا الخطأ لاحقأً في هذه الصفحة. قبل أن نصل إلى ذلك، دعنا نضيف بعض الهيكلية لبياناتك.

تصفية مصفوفات من العناصر

هذه البيانات يمكن أن تكون مهيكلةً أكثر من ذلك.

const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'mathematician',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'physicist',
}, {
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];

دعنا نقول بأنك تريد طريقة لإظهار الناس الذين مهنتهم هي 'chemist' فقط. تستطيع استخدام دالة JavaScript filter() لتقوم بإرجاع هؤلاء الناس فقط. هذه دالة تستقبل مصفوفة من العناصر، تمررهم عبر “test” (وهي دالة تقوم بإرجاع true أو false), وتقوم بإرجاع مصفوفة جديدة من هؤلاء العناصر فقط الذين اجتازوا الاختبار “الدالة test” (أي قامت بإرجاع true)

أنت تريد العناصر التي مهنتها profession هي chemist فقط. الدالة “test” لهذا الأمر تبدو كالآتي: (person) => person.profession === 'chemist'. هنا نجد كيفية وضعهم معاً:

  1. أنشئ مصفوفة جديدة تحوي الناس التي مهنتها “chemist” أو “عالم كيمياء” فقط, chemists, عن طريق استدعاء الدالة filter() على المصفوفة people وتصفيتهم حسب مهنتهم person.profession === 'chemist':
const chemists = people.filter(person =>
person.profession === 'chemist'
);
  1. الآن قم بغمل map على المصفوفة chemists:
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
known for {person.accomplishment}
</p>
</li>
);
  1. أخيراً, قم بإرجاع return القائمة listItems من مكونك:
return <ul>{listItems}</ul>;
import { people } from './data.js';
import { getImageUrl } from './utils.js';

export default function List() {
  const chemists = people.filter(person =>
    person.profession === 'chemist'
  );
  const listItems = chemists.map(person =>
    <li>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        known for {person.accomplishment}
      </p>
    </li>
  );
  return <ul>{listItems}</ul>;
}

Pitfall

الدوال السهمية Arrow function تعيد قيمة التعبير الموجود على اليمين بعد =>، لذلك لا حاجة للبيان return:

const listItems = chemists.map(person =>
<li>...</li> // Implicit return!
);

على أيّ حال، عليك كتابة return بشكل صريح عندما يتبع => خاصتك بقوس منحني {

const listItems = chemists.map(person => { // Curly brace
return <li>...</li>;
});

الدوال السهمية التي تتضمن => { تمتلك “block body”. يسمحون لك بكتابة أكثر من سطرٍ واحدٍ من الكود، لكن عليك كتابة البيان return بنفسك. إذا نسيته، لا شي يرجع من الدالة!

أبقي العناصر مرتبة مع key

لاحظ بأنّ كل snaboxes في الأعلى تظهر خطأً في ال console:

Console
Warning: Each child in a list should have a unique “key” prop.

أنت تحتاج إعطاء كل عنصر في المصفوفة key — نوهو عبارة عن نص أو فريد يميز العنصر عن العناصر الأخرى في تلك المصفوفة:

<li key={person.id}>...</li>

Note

عناصر JSX مباشرة داخل الدالة map() استدعائها يحتاج دائماً مفاتيح!

المفاتيح تخبر React بكل عنصر في المصفوفة مع المكون الموافق له، لذلك يصبح بإمكانها أن تجدهم لاحقاً. هذا الأمر يصبح بالغ الأهمية إذا استطاعت عناصر مصفوفتك التحرك (مثال ذلك الفرز)، أو إدخال عناصر أخرى، أو حذف عناصر من المصفوفة. الاختيار الجيد لل key يساعد React على استنتاج ما حدث بالضبط، وبالتالي القيام بالتحديثات الصحيحة على شجرة DOM.

بدلا من توليد مفاتيح بسرعة وإغفالها، يجب عليك تضمينهم في بياناتك:

export const people = [{
  id: 0, // Used in JSX as a key
  name: 'Creola Katherine Johnson',
  profession: 'mathematician',
  accomplishment: 'spaceflight calculations',
  imageId: 'MK3eW3A'
}, {
  id: 1, // Used in JSX as a key
  name: 'Mario José Molina-Pasquel Henríquez',
  profession: 'chemist',
  accomplishment: 'discovery of Arctic ozone hole',
  imageId: 'mynHUSa'
}, {
  id: 2, // Used in JSX as a key
  name: 'Mohammad Abdus Salam',
  profession: 'physicist',
  accomplishment: 'electromagnetism theory',
  imageId: 'bE7W1ji'
}, {
  id: 3, // Used in JSX as a key
  name: 'Percy Lavon Julian',
  profession: 'chemist',
  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',
  imageId: 'IOjWm71'
}, {
  id: 4, // Used in JSX as a key
  name: 'Subrahmanyan Chandrasekhar',
  profession: 'astrophysicist',
  accomplishment: 'white dwarf star mass calculations',
  imageId: 'lrWQx8l'
}];

Deep Dive

عرض عدة عقد DOM لكل قائمة من العناصر

ماذا تفعل عندما كل عنصر يحتاج التصيير إلي عدة عقد DOM وليس واحداً منها فقط?

الصيغة القصيرة ل <>...</> Fragment لا تسمح لك بتمرير مفتاح، لذلك تحتاج إمّا أن تجمعهم داخل عنصر <div> مفرد، أو استخدام الصيغة الطويلة قليلاً و الأكثر صراحة <Fragment>:

import { Fragment } from 'react';

// ...

const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);

Fragments تختفي من DOM, لذلك سوف تنتج قائمة منبسطة (لاتحوي قوائم داخلية) مكونة من <h1>, <p>, <h1>, <p>, إلخ.

من أين تحصل على key الخاص بك

المصادر المخلتفة للبيانات تقدم مصادر مختلفة للمفاتيح:

  • البيانات من قواعد البيانات: إذا كانت بياناتك قادمة من قاعدة بيانات، يمكنك استخدام keys/IDs الخاصة بقاعدة البيانات, والتي هي فريدة بطبيعتها.
  • البيانات التي تم توليدها محلياً: إذا كانت بياناتك مولدة ومستمرة محلياً (الملاحظات في تطبيق تدوين الملاحظات)، تستخدم عداد متزايد، crypto.randomUUID() أو حزمة مثل uuid عندما تنشأ العناصر.

قواعد المفاتيح

  • المفاتيح يجب أن تكون فريدة بين الأشقاء. على أي حال، إنه من الصحيح استخدام المفاتيح نفسها لعقد JSX في مصفوفات مختلفة.
  • المفاتيح لا يجب أن تتغير أو إحباط أهدافها! لا تقم بتوليدهم خلال التصيير.

لماذا تحتاج React مفاتيح?

تخيل بأنّ الملفات على حاسوبك لا تمتلك أسماء. بدلاً من ذلك أنت ترجع إليهم عن طريق ترتيبهم — الملف الأول، الملف الثاني، إلخ. يمكنك اعتياد ذلك، لكن بمجرد حذفك لملف، يمكن أن يصبح الأمر غير مقبول. الملف الثاني يصبح الملف الاول، الملف الثالث يصبح الملف الثاني، وهكذا.

أسماء الملفات في مجلد و مفاتيح JSX في مصفوفة تخدم نفس الهدف. إنها تمكننا من تحديد عنصر بشكل فريد بين أشقائه. الاختيار الجيد للمفتاح يقدم معلومات أكثر من الموضع خلال المصفوفة. وحتى لو تغير الموضع بسبب إعادة الترتيب، ال key يمكن React من تحديد العنصر خلال حياته.

Pitfall

يمكن أن يكون استخدام فهرس العنصر في مصفوفة كمفتاح له مغرياً . في الحقيقة، ذلك ما ستستخدمه React إذا لم تحدد key على الإطلاق. لكن الترتيب المستخدم في تصيير العناصر سوف يتغير بمرور الوقت إذا تم إدخال عنصر، حذفه أو إذا تغير ترتيب المصفوفة. استخدام الفهرس كمفتاح سوف يؤدي إلى مشاكل خفية ومرفوضة في الكود.

بالمثل لا تقلل من أهمية المفاتيخ عند توليدها، كاستخدام key={Math.random()}. هذا سيجعل المفاتيح غير متطابقة أبداً بين التصييرات، قائدة بذلك كل مكوناتك و DOM إلى أن يعاد إنشاؤها في كل مرة. ليس هذا بطيئاً فقط، بل سيفقد أي بيانات مدخلة من المستخدم داخل عناصر القائمة أيضاً، استخدم ID مستقراً معتمداً على البيانات.

لاحظ أن مكوناتك لا تتلقى key كخاصية prop. إنها تستخدم فقط كتلميح ل React نفسه. إذا احتاج مكونك ID، عليك تمريره كخاصية منفصلة: <Profile key={id} userId={id} />.

Recap

تعلمت في هذه الصفحة:

  • كيف تحول البيانات إلى مكونات أو إلى هياكل مثل المصفوفات والكائنات.
  • كيف تولد مجموعات من المكونات المتشابهة باستخدام دالة JavaScript map().
  • كيف تنشأ مصفوفات من عناصر مصفّاة باستخدام دالة JavaScript filter().
  • لماذا وكيف تضبط ال key لكل مكون في مجموعة بطريقة تستطيع فيها React أن تتعقب كل واحد منهم حتى لو تغيرت بياناتهم ومواضعهم.

Challenge 1 of 4:
فصل قائمة في قائمتين

هذا المثال يظهر قائمة بجميع الناس.

أجري تغيرات عليها إظهار قائمتين منفصلتين واحدة بعد اأخرى: Chemists و Everyone Else. مثل ما سبق, يمكنك تحديد فيما إذا كان الشخص عالم كيمياء عن طريق التحقق من صحة person.profession === 'chemist'.

import { people } from './data.js';
import { getImageUrl } from './utils.js';

export default function List() {
  const listItems = people.map(person =>
    <li key={person.id}>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        known for {person.accomplishment}
      </p>
    </li>
  );
  return (
    <article>
      <h1>Scientists</h1>
      <ul>{listItems}</ul>
    </article>
  );
}