تکرار عناصر (حلقه ها) در ری اکت

17 فروردین 1398
درسنامه درس 13 از سری آموزش react (ری اکت)
React-loops

تا به اینجای آموزش ما توانستیم یک برنامه ساده را بدون استفاده از داده های خارجی ایجاد کنیم. حال می خواهیم با مطالبی که تاکنون آموختیم، پروژه ی جدیدی را شروع کنیم.

تکرار کردن عناصر

در قسمت های قبلی دیدیم که چطور می شود در بین لیستی از اشیاء پیمایش کرد و چندین کامپوننت را در صفحه رندر کنیم.

قبل از اینکه با افزودن داده های خارجی، پیچیدگی برنامه مان را زیاد کنیم، مروری خواهیم داشت بر چگونگی تکرار عناصر (کامپوننت) در یک برنامه.

به دلیل اینکه مرورگر، پسوند jsx را تنها به عنوان کدهای جاوا اسکریپت ساده می بیند، تنها می توانیم از دستورات جاوا اسکریپت در تگ قالب ({}) استفاده کنیم. کدهای زیر را ببینید:

const App = (props) => {
  return (
    <ul>
      {a.map(i => {
        return <li>{i}</li>
      })}
    </ul>
  )
}

اگر به تگ قالب ({ }) نگاه کنید می بینید که تنها شامل دستورات جاوا اسکریپت است. ما می توانیم از هر نوع دستور جاوا اسکریپتی مثل تکرارکننده های (iterator) بومی مثل map و forEach در آن استفاده کنیم.

بیایید ببینیم منظورمان چیست؟ درمثال قبلی مقدار متغیر a که از نوع عددی (integer) بود به لیستی از اعداد تغییر می دهیم:

const a = [1, 10, 100, 1000, 10000];

حال می توانیم توسط متد map لیست فوق را پیمایش کرده و یک لیست جدیدی از کامپوننت های ری اکت که DOM مجازی (virtual DOM) را خواهند ساخت، ایجاد کنیم.

در صورتیکه با مفهوم DOM آشنایی ندارید، مقاله زیر بهترین منبع مورد نظر می باشد. حتما آن را مطالعه کنید:

const App = (props) => {
  return (
    <ul>
      {a.map(i => {
        return <li>{i}</li>
      })}
    </ul>
  )
}

تابع map چطور کار می کند؟

تابع map یکی از توابع داخلی جاوا اسکریپت است که بر روی آرایه ها استفاده می شود. این تابع یک کالبک (یا تابع) را به عنوان پارامتر گرفته و آن را بر روی تمام عناصر آرایه اجرا می کند. در مثال فوق این تابع چهار بار اجرا می شود و در ابتدا مقدار i برابر 1 بوده و سپس با اجرای بعدی برابر 10 و تا به آخر، خواهد شد.

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

ابتدا فایل src/App.js را باز کرده و محتوای کامپوننت App را با این سورس جایگزین می کنیم و متغیرهای اضافی را هم حذف می کنیم. کدهای src/App.js باید مطابق زیر باشد:

import React from 'react';

const a = [1, 10, 100, 1000, 10000];
const App = (props) => {
  return (
    <ul>
      {a.map(i => {
        return <li>{i}</li>
      })}
    </ul>
  )
}

export default App

ترمینال را باز کرده و با دستور npm start برنامه را اجرا کنید.

نمایش عناصر آرایه توسط متد map در ری اکت

اگر developer console مرورگر را باز کنید، می بینید که یک پیام خطا در آن نمایش داده می شود. این خطا به این خاطر است که ری اکت نمی تواند کامپوننت های منحصر به فرد در لیست را تشخیص دهد. برای افزایش کارایی، ری اکت با استفاده از DOM مجازی سعی می کند تا تعداد عناصر DOM که در هنگام نمایش ویوها نیاز است را محدود کند. اگر تغییری اتفاق نیفتد، ری اکت هیچ بروزرسانی در مرورگر انجام نمی دهد.

این کار یک ویژگی خوب برای ساخت برنامه های تحت وب است، اما گاهی اوقات باید یک id منحصر به فرد برای عناصرمان اضافه کنیم. پیمایش در یک لیست و نمایش کامپوننت ها در متد map یکی از آن مواقعی است که باید از این قابلیت استفاده کنیم.

ری اکت یک پروپرتی ویژه به نام key دارد که به هر کدام از عناصر یک لیست اختصاص می یابد.

مقدار پروپرتی key هر چیزی می تواند باشد، اما باید برای آن عنصر منحصر به فرد باشد. در این مثال می توانیم از مقدار متغیر i به عنوان یک کلید منحصر به فرد استفاده کنیم، چون این آرایه هیچ مقدار تکراری ندارد. حال کد بالا را مطابق زیر بروزرسانی می کنیم:

const App = (props) => {
  return (
    <ul>
      {a.map(i => {
        return <li key={i}>{i}</li>
      })}
    </ul>
  )
}

 عنصرهای فرزند

در آموزش های قبلی درباره ساخت رابطه والد-فرزند صحبت کردیم، اما حالا می خواهیم ببینیم که چطور می شود در داخل کامپوننت والد به کامپوننت فرزند دسترسی داشته باشیم و چطور آنها را رندر کنیم.

در درس یازدهم، یک کامپوننت <Formatter/> برای مدیریت فرمت بندی تاریخ در داخل کامپوننت clock ایجاد کردیم تا به کاربرانمان این امکان را بدهیم که ساعت را مطابق نیاز خود سفارش سازی کنند.

همان طور که در زیر می بینید، کدها کمی نامرتب و زشت به نظر می رسد:

const Formatter = (props) => {
  let children = props.format.split('').map((e, idx) => {
    if (e === 'h') {
      return <Hour key={idx} {...props} />
    } else if (e === 'm') {
      return <Minute key={idx} {...props} />
    } else if (e === 's') {
      return <Second key={idx} {...props} />
    } else if (e === 'p') {
      return <Ampm key={idx} {...props} />
    } else if (e === ' ') {
      return <span key={idx}> </span>;
    } else {
      return <Separator key={idx} {...props} />
    }
  });

  return <span>{children}</span>;
}

حال می توانیم از آبجکت React.children برای پیمایش در میان لیستی از آبجکت های ری اکت استفاده کنیم. همان طور که می بینید، کدهای زیر ظاهر بهتری دارند.

const Formatter = ({format, state}) => {
  let children = format.split('').map(e => {
    if (e == 'h') {
      return <Hour />
    } else if (e == 'm') {
      return <Minute />
    } else if (e == 's') {
      return <Second />
    } else if (e == 'p') {
      return <Ampm />
    } else if (e == ' ') {
      return <span> </span>;
    } else {
      return <Separator />
    }
  });
  return (<span>
      {React.Children
        .map(children, c => React.cloneElement(c, state))}
      </span>)
}

React.cloneElement

در درس های قبلی گفتیم که مرورگرها کدهای jsx را به جاوا اسکریپت تبدیل می کنند، مانند زیر:

React.createElement("div", null, 
 React.createElement("img", {src: "profile.jpg", alt: "Profile photo"}),
 React.createElement("h1", null, "Welcome back Ari")
);

بجای ساخت یک کامپوننت جدید، گاهی اوقات می خواهیم یک کپی از یک کامپوننت بگیریم و یا پروپرتی یا فرزندهایی را به آن اضافه و همان پروپرتی هایی که دارد را حفظ کنیم. برای این منظور از React.cloneElement استفاده می کنیم.

React.cloneElement()، api هایی مشابه با React.createElement دارد و آرگومان های آن عبارتند از:

  • عنصر ری اکتی که می خواهیم از آن یک کپی (clone) بگیریم
  • prop هایی که می خواهیم به این نمونه اضافه کنیم
  • عنصر فرزندی که می خواهیم داشته باشیم

در مثال Formatter یک کپی از تمام فرزندان لیست ایجاد ( که شامل کامپوننت های <Home/> و <Minute/> و... می شد) و توسط props آنها را به آبجکت state ارسال کردیم.

آبجکت React.children تعدادی تابع کاربردی برای کار با عنصرهای فرزند دارد. در مثال Formatter بالا، با استفاده از تابع map تمام عنصرهای فرزند را پیمایش کرده و یک کپی از آنها را در لیست می ریزیم. سپس برای هر کدام از آنها در صورت لزوم یک key تعیین می کنیم.

حال با استفاده از تابع ()React.children.map کامپوننت App را بروزرسانی می کنیم:

const App = (props) => {
  return (
    <ul>
      {React.Children.map(a, i => <li>{i}</li>)}
    </ul>
  )
}

نتیجه اجرا در مرورگر:

نمایش عناصر آرایه توسط متد map در ری اکت
نمایش عناصر آرایه توسط متد map در ری اکت

React.children چند متد کاربردی دیگری هم دارد. ما در این آموزش از روش React.children.map بیشتر از بقیه استفاده کردیم، اما برای مشاهده متدهای دیگر به این آدرس مراجعه کنید.

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

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش react (ری اکت) توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.