لیست‌ها و کلید‌‌ها

ابتدا اجازه دهید به بررسی نحوه تبدیل لیست‌ها در جاوا‌اسکریپت بپردازیم.

با توجه به کد زیر، ما از تابعmap() استفاده می‌کنیم تا آرایه‌‌ای از اعداد با نام numbers را گرفته و مقادیرش را دو برابر کند. آرایه‌ی جدیدی که توسط map() برگشته را به متغیر doubledاختصاص می‌دهیم و آن را چاپ می‌کنیم.

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);

این کد [2, 4, 6, 8, 10] را در کنسول چاپ می‌کند.

در ری‌اکت تبدیل آرایه‌ها به لیستی از المنت‌ها به صورت مشابه انجام می‌شود.

رندر کردن چندین کامپوننت‌

می‌توانید با استفاده از آکولاد {}، مجموعه‌ای از المنت‌ها را بسازید و آن‌ها را در JSX درج کنید.

در قسمت زیر ما با استفاده از تابع map() در جاوا‌اسکریپت، بر‌روی آرایه‌ ی‌ numbersحلقه می‌زنیم. برای هر آیتم یک المنت <li> برمی‌گردانیم و در نهایت نتیجه‌ی آرایه‌ی المان‌ها را به listItems اختصاص می‌دهیم. :

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li>{number}</li>
);

کل آرایه‌ی listItems را درون یک المنت <ul> قرار می‌دهیم و آن را در DOM رندر می‌کنیم:

ReactDOM.render(
  <ul>{listItems}</ul>,
  document.getElementById('root')
);

در کدپن امتحان کنید

این کد یک لیست گلوله‌ای از اعداد بین ۱ تا ۵ را نشان می‌دهد.

کامپوننت لیست ساده

معمولا شما لیست‌ها را درون یک کامپوننت رندر خواهید کرد.

می‌توانیم مثال قبلی را طوری بیهنه‌سازی کنیم که یک کامپوننت آرایه‌ ی numbers را بگیرد و لیستی از المنت‌ها را باز گرداند.

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li>{number}</li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

وقتی این کد را اجرا کنید، با این هشدار مواجه می‌شوید که برای آیتم‌های لیست یک key لازم است. “key” یک ویژگی خاص از جنس رشته است که وقتی لیستی از المنت‌ها درست می‌کنید، باید از آن استفاده کنید. در بخش بعدی دلیل اهمیت آن را توضیح می‌دهیم.

بیاید به آیتم‌های لیست خود در numbers.map()، یک key اختصاص دهیم و مشکل نبودن این key را حل کنیم.

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

در کدپن امتحان کنید

کلیدها

کلیدها در ری‌اکت به شناسایی این که کدام آیتم‌ها تغییر کرده‌اند، اضافه و یا حذف شده اند کمک میکند. کلیدها باید به المنت‌های داخل آرایه داده شوند تا به المنت‌ها یک شناسه ی ثابت بدهند.

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li key={number.toString()}>
    {number}
  </li>
);

بهترین راه برای انتخاب یک کلید، استفاده از یک رشته است که یک مورد از لیست را میان برادرانش متمایز می‌کند. اغلب شما از IDهای داده خود به عنوان کلید استفاده خواهید کرد.

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

زمانی که برای آیتم‌های رندر شده یک شناسه ثابت نداشته‌باشید، شما می‌توانید به عنوان آخرین راه چاره از index آیتم به عنوان کلید استفاده کنید:

const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  // فقط زمانی این کار را انجام دهید که آیتم ها شناسه ی پایداری ندارند
  <li key={index}>
    {todo.text}
  </li>
);

اگر ممکن است ترتیب آیتم‌ها تغییر کند، پیشنهاد نمی‌کنیم که برای کلیدها از شاخص استفاده کنید. زیرا ممکن است تاثیر منفی‌ای بر عملکرد بگذارد و حتی باعث ایجاد مشکلاتی در state کامپوننت شود. مقاله رابین پوکرنی (Robin Pokorny) که توضیحی مفصل در مورد تاثیر منفی استفاده از شاخص به عنوان یک کلید است را بخوانید.

اگر علاقه‌مندید بیشتر بدانید مقاله ی توضیح مفصل در مورد اینکه چرا کلید ضروری است را بخوانید.

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

استفاده از keyها زمانی معنی می‌دهد که [کد] در یک آرایه احاطه شده‌باشد.

برای مثال اگر یک تکه از کد را به عنوان کامپوننت ListItem جدا کنید، باید کلید را روی کامپوننت‌های <listitem />قرار دهید، نه روی المنت <li> که درون خود ListItem است.

مثال: استفاده‌ی اشتباه از کلید

function ListItem(props) {
  const value = props.value;
  return (
   // اشتباه! اینجا احتیاجی به مشخص کردن کلید نیست: 
    <li key={value.toString()}>
      {value}
    </li>
  );
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // اشتباه! اینجا باید کلید مشخص میشد:
    <ListItem value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

مثال: استفاده‌ی صحیح از کلید

function ListItem(props) { 
  // صحیح!اینجا احتیاجی به مشخص کردن کلید نیست:
  return <li>{props.value}</li>;
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // Correct! Key should be specified inside the array.
    <ListItem key={number.toString()}
              value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

در کدپن امتحان کنید

یک قاعده خوب این است که المنت‌های داخل فراخوانیmap() نیاز به کلید دارند.

کلید‌ها فقط باید میان همتایان خود یکتا باشند

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

function Blog(props) {
  const sidebar = (
    <ul>
      {props.posts.map((post) =>
        <li key={post.id}>
          {post.title}
        </li>
      )}
    </ul>
  );
  const content = props.posts.map((post) =>
    <div key={post.id}>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
  );
  return (
    <div>
      {sidebar}
      <hr />
      {content}
    </div>
  );
}

const posts = [
  {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
  {id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
  <Blog posts={posts} />,
  document.getElementById('root')
);

در کدپن امتحان کنید

ری‌اکت از keyها به عنوان یک راهنما استفاده می‌کند، اما به کامپوننت‌های شما داده نمی‌شوند. اگر به همان کلید در کامپوننت خود احتیاج دارید، به صورت آشکار آن‌را به عنوان یک prop با یک نام متفاوت پاس دهید.

const content = posts.map((post) =>
  <Post
    key={post.id}
    id={post.id}
    title={post.title} />
);

در مثال بالا، کامپوننت Post می‌تواند props.id را بخواند، اما props.key را نه.

جاسازی map() در JSX

در مثال بالا، ما متغیر listItems را جداگانه تعریف کردیم و آن‌را در JSX قرار دادیم:

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <ListItem key={number.toString()}
              value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

JSX اجازه استفاده توکار هر expression را در آکولادها می‌دهد بنابراین می‌توان نتیجه map() را درخط (inline) نوشت:

function NumberList(props) {
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number) =>
        <ListItem key={number.toString()}
                  value={number} />
      )}
    </ul>
  );
}

در کدپن امتحان کنید

گاهی اوقات این‌کار باعث واضح‌تر شدن کد میشود، اما این سبک نیز می‌تواند بد استفاده شود. همانند جاوااسکریپت، تصمیم اینکه یک متغیر را برای خوانایی بهتر استخراج کنید یا نه، به شما بستگی دارد. به خاطر داشته باشید که اگر بدنه map() بیش از حد تودرتو شد، ممکن است زمان خوبی باشد که از آن یک کامپوننت استخراج کنید.