رندر کردن لیست

اغلب اوقات می‌خواهید چندین کامپوننت مشابه را از یک مجموعه داده نمایش دهید. می‌توانید از متدهای آرایه جاوااسکریپت برای مدیریت یک آرایه از داده‌ها استفاده کنید. در این صفحه، از متدهای filter() و map() همراه با React استفاده خواهید کرد تا آرایه داده‌های خود را فیلتر کرده و به آرایه‌ای از کامپوننت‌ها تبدیل کنید.

You will learn

  • چگونه کامپوننت‌ها را با استفاده از متد ()map جاوااسکریپت از یک آرایه رندر کنیم
  • چگونه فقط کامپوننت‌های خاص را با استفاده از متد ()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>

تنها تفاوت بین آیتم‌های این لیست، محتوای آنها یا داده‌هایشان است. هنگام ساخت رابط‌های کاربری، اغلب نیاز خواهید داشت که چندین نمونه از یک کامپوننت را با داده‌های مختلف نمایش دهید؛ از لیست نظرات گرفته تا گالری‌های تصاویر پروفایل. در این موارد، می‌توانید داده‌ها را در آبجکت‌ها و آرایه‌های جاوااسکریپت ذخیره کنید و از متدهایی مانند map() و 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. اعضای 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>;
}

توجه کنید که سندباکس بالا یک خطای کنسول نمایش می‌دهد:

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' است نمایش دهد. می‌توانید از متد filter() جاوااسکریپت برای بازگشت فقط این افراد استفاده کنید. این متد یک آرایه از آیتم‌ها را می‌گیرد، آنها را از طریق یک “آزمون” (یک تابع که true یا false را برمی‌گرداند) عبور می‌دهد و یک آرایه جدید از فقط آیتم‌هایی که آزمون را پشت سر گذاشته‌اند (و true برگشت داده‌اند) باز می‌گرداند.

شما فقط آیتم‌هایی را می‌خواهید که profession آنها 'chemist' باشد. تابع “آزمون” برای این کار به شکل (person) => person.profession === 'chemist' خواهد بود. در اینجا نحوه ترکیب آن آمده است:

  1. یک آرایه جدید از افراد با حرفه “chemist” به نام chemists ایجاد کنید با فراخوانی متد filter() بر روی آرایه people و فیلتر کردن بر اساس person.profession === 'chemist':
const chemists = people.filter(person =>
person.profession === 'chemist'
);
  1. حالا بر روی chemists از متد map() استفاده کنید:
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. در نهایت، آرایه 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

توابع پیکانی به طور ضمنی عبارت بعد از => را باز می‌گردانند، بنابراین نیازی به دستور 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

توجه کنید که همه‌ی سندباکس‌های بالا یک خطا در کنسول نمایش می‌دهند:

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>
);

فرگمنت‌ها از DOM ناپدید می‌شوند، بنابراین این کار یک لیست صاف از <h1>, <p>, <h1>, <p>, و به همین ترتیب تولید خواهد کرد.

کجا باید کلید خود را بگیرید؟

منابع مختلف داده، منابع مختلفی برای کلیدها فراهم می‌کنند:

  • داده‌ها از یک پایگاه داده: اگر داده‌های شما از یک پایگاه داده می‌آید، می‌توانید از کلیدها/شناسه‌های پایگاه داده استفاده کنید که به طور طبیعی منحصر به فرد هستند.
  • داده‌های تولید شده به صورت محلی: اگر داده‌های شما به صورت محلی تولید و ذخیره می‌شود (مثلاً یادداشت‌ها در یک اپلیکیشن یادداشت‌برداری)، از یک شمارنده افزایشی، crypto.randomUUID() یا یک پکیج مانند uuid هنگام ایجاد آیتم‌ها استفاده کنید.

قوانین کلیدها

  • کلیدها باید در میان دسته خود منحصر به فرد باشند. اما استفاده از کلیدهای مشابه برای نودهای JSX در آرایه‌های متفاوت اشکالی ندارد.
  • کلیدها نباید تغییر کنند زیرا این کار هدف آنها را بی‌اثر می‌کند! از تولید آن‌ها در حین رندرینگ خودداری کنید.

چرا React به کلیدها نیاز دارد؟

تصور کنید که فایل‌ها در دسکتاپ شما نام نداشته باشند. در عوض، شما آنها را بر اساس ترتیبشان ارجاع می‌دهید — فایل اول، فایل دوم و به همین ترتیب. شاید به این موضوع عادت کنید، اما زمانی که یک فایل حذف شود، این موضوع گیج‌کننده می‌شود. فایل دوم به فایل اول تبدیل می‌شود، فایل سوم به فایل دوم و همینطور ادامه می‌یابد.

نام فایل‌ها در یک پوشه و کلیدهای JSX در یک آرایه هدف مشابهی دارند. آنها به ما این امکان را می‌دهند که یک آیتم را بین هم‌نیاکانش به طور منحصر به فرد شناسایی کنیم. یک کلید به‌خوبی انتخاب شده اطلاعات بیشتری نسبت به موقعیت داخل آرایه فراهم می‌کند. حتی اگر موقعیت به دلیل مرتب‌سازی تغییر کند، کلید به React این امکان را می‌دهد که آیتم را در طول عمر آن شناسایی کند.

Pitfall

ممکن است وسوسه شوید که از ایندکس آیتم در آرایه به عنوان کلید آن استفاده کنید. در واقع، این همان چیزی است که React در صورتی که شما هیچ کلیدی مشخص نکنید، استفاده خواهد کرد. اما ترتیب رندر کردن آیتم‌ها با گذشت زمان تغییر خواهد کرد اگر یک آیتم وارد شود، حذف شود یا اگر آرایه دوباره مرتب شود. استفاده از شاخص به عنوان کلید اغلب منجر به باگ‌های ظریف و گیج‌کننده می‌شود.

به طور مشابه، از تولید کلیدها به صورت آنی خودداری کنید، مثلاً با key={Math.random()}. این کار باعث می‌شود که کلیدها هیچ وقت بین رندرها مطابقت نداشته باشند و منجر به بازسازی تمامی کامپوننت‌ها و DOM در هر بار رندر شود. این نه تنها کند است، بلکه هر ورودی کاربر در داخل آیتم‌های لیست را نیز از دست می‌دهد. در عوض، از یک شناسه ثابت مبتنی بر داده‌ها استفاده کنید.

توجه داشته باشید که کامپوننت‌های شما key را به عنوان یک پراپ دریافت نمی‌کنند. این فقط توسط React به عنوان یک راهنمایی استفاده می‌شود. اگر کامپوننت شما به یک شناسه نیاز دارد، باید آن را به عنوان یک پراپ جداگانه ارسال کنید: <Profile key={id} userId={id} />.

Recap

در این صفحه شما آموختید:

  • چگونه داده‌ها را از کامپوننت‌ها خارج کرده و به ساختارهای داده‌ای مانند آرایه‌ها و اشیاء منتقل کنید.
  • چگونه مجموعه‌هایی از کامپوننت‌های مشابه با استفاده از متد map() در جاوااسکریپت ایجاد کنید.
  • چگونه آرایه‌هایی از آیتم‌های فیلتر شده با استفاده از متد filter() در جاوااسکریپت بسازید.
  • چرا و چگونه باید برای هر کامپوننت در یک مجموعه key را تنظیم کنید تا React بتواند هر کدام را پیگیری کند حتی اگر موقعیت یا داده‌های آن‌ها تغییر کند.

Challenge 1 of 4:
تقسیم یک لیست به دو بخش

این مثال یک لیست از تمام افراد را نشان می‌دهد.

آن را تغییر دهید تا دو لیست جداگانه یکی پس از دیگری نمایش داده شود: شیمیدان‌ها و بقیه افراد. همانطور که قبلاً اشاره شد، می‌توانید تعیین کنید که آیا یک شخص شیمیدان است یا خیر با بررسی اینکه 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>
  );
}