از سیر تا پیاز ری‌اکت، نکست و ریداکس!

عمیق‌تر در مورد جاوااسکریپت، ری‌اکت و نکست‌جی‌اس بدونیم

نکست‌جی‌اس و ریداکس‌تانک، از مبتدی تا پیشرفته

مقدمه

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

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

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

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

سپاس.

جاوا‌اسکریپت و اکما‌اسکریپت (ECMAScript)

اولین چیزی که لازم هست بدونید، اینه که جاوا‌اسکریپت توسط شرکت نِت‌اِسکیِپ (شرکت قدیمی موزیلا فایرفاکس) ساخته و به سازمان اِکما داده شد تا اون رو استانداردسازی کنن. اِکما سازمانیه که وظیفش استانداردسازی اطلاعاته.

اونچه که در نهایت از جاوا‌اسکریپت توسط سازمان اِکما ارائه شد، اِکما‌اِسکریپت بود. ساده‌تر بگم، اِکما‌اِسکریپت یک استاندارد هست، در حالی که جاوا‌اسکریپت محبوب‌ترین پیاده‌سازی از اون استاندارد به حساب میاد. جاوا‌اِسکریپت، اِکما‌اِسکریپت رو پیاده‌سازی میکنه و بر پایه اون ساخته میشه.

حالا سوالی که مطرح میشه، اینه که “ES” چیه؟

ای‌اِس، مخفف اِکما‌اِسکریپته (ECMAScript)، هرجایی که ای‌اِس رو در کنار یک عدد دیدید، مثل ای‌اس۶، یادتون باشه که داره به یک نسخه از اِکما‌اِسکریپت یا در واقع یک نسخه از استاندارد اشاره میشه.

ای‌اِس (ES)

ای‌اِس تا به اینجا ۸ نسخه مختلف رو ارائه کرده، ای‌اِس۱، ۲، ۳ و ۴ بین سال‌های ۱۹۹۷ تا ۱۹۹۹ ارائه شدن و دیگه ازشون پشتیبانی نمیشه. (ما هم کاری بهشون نداریم).

ای‌اِس۵ تقریبا ۱۰ سال بعد از اس‌اس۴ یعنی اواخر سال ۲۰۰۹ (تاریخ دقیق رو نمیدونم) ارائه داده شد.

ای‌اِس۶ در سال ۲۰۱۵ منتشر شد که برای راحتی کار، بهش ای‌اس۲۰۱۵ هم میگن. در واقع چون ای‌اس۶ در سال ۲۰۱۵ ارائه شده، بهش ای‌اِس۲۰۱۵ میگن!

ای‌اِس۷/ای‌اس۲۰۱۶ که مطمئنا میتونید پیشبینی کنید در سال ۲۰۱۶ منتشر شد.

ای‌اس۸/ای‌اس۲۰۱۷ هم، در سال ۲۰۱۷ منتشر شد.

چرا ورژن‌های مختلف از ای‌اس منتشر شدن؟

دلیلش اینه که هر وِرژِن، ویژگی‌های جدیدی رو ارائه کرده که با زمان خودش هم‌خوانی داشته باشه. فقط باید چند نکته رو به خاطر داشته‌باشید:

  1. پیشبینی میشه که هر سال یک نسخه جدید از اِکما‌اِسکریپت ارائه بشه،
  2. اولین نسخه‌های اِکما‌اِسکریپت با عدد نسخه‌بندی میشدن، مثل ای‌اِس۱، ای‌اِس۲ و…
  3. نسخه‌های جدید که از سال ۲۰۱۵ شروع شدن، به شکل ای‌اس[سال انتشار] نام‌گذاری میشن.
  4. اکما یک استاندارده، جاوا‌اسکریپت محبوب‌ترین پیاده‌سازی از اون استاندارد هست.

چرا ای‌اس۶؟

نسخه ۶‌ام از اِکما‌اِسکریپت، ویژگی‌های خیلی خوبی رو به زبان جاوا‌اسکریپت اضافه کرد، و همین، یکی از دلایلی شد که ری‌اَکت به طور پیش‌فرض ازش پشتیبانی میکنه. مثلا مفاهیمی مثل «کلاس‌ها» و «ماژول‌ها»، که برای زبان‌های شئ‌گرا اجباری هستند، بهش اضافه شدن. از جمله ویژگی‌های دیگش، اضافه شدن دستورات for، جِنِراتور‌های شبیه به زبان پایتون، توابع فِلِشی (Arrow Function)، کالِکشِن‌ها، پرامِس‌ها (Promise) و غیره بوده.

متاسفانه هنوز مرورگرها پشتیبانی از ای‌اِس۶ رو کامل نکردن و ای‌اِس۶ به خودی خود تو مرورگرها پشتیبانی نمیشه. اینجاست که مفاهیم تِرَنس‌پایلِرها (transpiler) خودشون رو نشون میدن که بعد از توضیح توابع فِلِشی (برای راحتی کار میگم اَرو فانکشِن) بهشون میپردازم.

Arrow Functions

یکی از ویژگی‌های خیلی خوبی که توی ای‌اِس۶ وجود داره، استفاده از اَرو فانکشن‌هاست. به اسمش دقت نکنید، مفهومش خیلی سادست. ارو فانکشن‌ها، در واقع همون توابع قدیمی جاوااسکریپت هستند (با ویژگی‌های جدید‌تر که اینجا بهشون کاری نداریم) که تو ای‌اس۶ به شکل دیگه‌ای تعریف میشن و موقع تِرَنس‌پایل (یکمی پایینتر توضیح میدم در این مورد) به شکل توابع جاوااسکریپت درمیان، مثلا:

x => {
  return x * x;
}

(m, n) => {
  return m+n
}

data => {
  data.json()
  .then(result => {
    return result;
  })
}

بعد از تِرَنس‌پایل تبدیل میشن به:

(function (x) {
  return x * x;
});

(function (m, n) {
  return m + n;
});

(function (data) {
  data.json().then(function (result) {
    return result;
  });
});

ترنس‌پایلر (transpiler)

حتما تا به حال واژه‌های «کامپایلِر» و «مفسر» به گوشتون خورده. تِرَنس‌پایلِرها در واقع نوعی کامپایلر هستند با یک تفاوت اصلی:

کامپایلرها معمولا زبان رو به یک نسخه قابل اجرا برای ماشین تبدیل می‌کنن، مثلا زبان سی، کدهای باینری یا همون صفر و یک میسازه، یا جاوا بایت‌کد رو تولید میکنه.

این درحالیه که ترنس‌پایلرها، یک سورس‌کد رو به یک سورس‌کد دیگه تبدیل می‌کنن (یا مثلا به یک زبان دیگه که مستقیم برای ماشین قابل درک نیست و باید دوباره کامپایل، اینتِرپرِت یا همون تفسیر بشه). مثلا کافی‌اسکریپت (CoffeeScript) که از خودش جاوا‌اسکریپت تولید میکنه، یا بابِل (Babel) که ای‌اس۶ رو به ای‌اس۵ (قابل پشتیبانی برای مرورگرها) تبدیل می‌کنه.

بابِل یا بِیبِل (Babel) یک ترنس‌پایلر برای ای‌اس۶ هست که اون رو به ای‌اس۵ تبدیل میکنه. ای‌اس۵ توسط مرورگرها خیلی خوب پشتیبانی میشه و در حقیقت، ای‌اس۵ همون جاوا‌اسکریپتیه که عموما باهاش آشنایی دارن.

بابِل، بِیبِل…؟ (Babel)

بابل یک تِرَنس‌پایلِر برای جاوا‌اسکریپته. بابل رو اکثرا بخاطر توانایی خوبش تو تبدیل ای‌اس۶ به ای‌اس۵ میشناسن.

به عنوان مثال این کد که با ای‌اس۶ نوشته شده:

let input = \[1, 2, 3\];
console.log(input.map(item => item + 1)); // \[2, 3, 4\]

توسط بابل تبدیل میشه به کد ای‌اس۵:

var input = \[1, 2, 3\];
console.log(input.map(function (item) {
  return item + 1;
})); // \[2, 3, 4\]

که تقریبا همه‌جا قابل اجراست. بابل خیلی خوب از پُلی‌فیل‌های جاوا‌اسکریپت پشتیبانی میکنه و باعث میشه که کد جاوا‌اسکریپت ساخته شده برای مرورگرهای قدیمی هم قابل پشتیبانی باشه. به همین خاطر، بابل باعث میشه شما از تمام ویژگی‌های ای‌اس۶ استفاده کنید بدون اینکه پشتیبانی از نسخه‌های قدیمی مرورگرها رو از دست بدید (مفهوم پُلی‌فیل اینجا معلوم میشه).

فرانت‌اند و مفاهیم اِس‌پی‌اِی (SPA)

اطلاعاتی که ما در اینترنت میبینیم، مجموعه‌ای از کدهای HTML، JavaScript و CSS هستن. قدیم‌ها، زمانی که خیلی از برنامه‌نویس‌ها از PHP (هنوز هم استفاده میکنن) برای نوشتن صفحات وب استفاده می‌کردن و جِی‌اِس مثل امروز محبوب نبود، هر صفحه از سایت باید بصورت جداگانه نوشته می‌شد. و زمانی که کاربر روی یک لینک کلیک میکرد، کل صفحه از اول رِندِر (Render) می‌شد. این فرایند برای کاربر، خسته‌کننده و طاقت‌فرسا بود و حتی گاهی بخاطر زمان زیادی که باید برای بارگذاری صفحه صرف میکرد، از ادامه کارش پشیمون میشد.

کم‌کم تکنیک‌هایی مثل اِی‌جَکس (AJAX) استفاده شدن، که مثلا وقتی صفحه در حال بارگذاری بود، شروع میکرد یک نماد کوچک لودینگ و بارگذاری (بهش معمولا میگن اِسپینِر یا spinner) رو نشون دادن و کاربر رو متوجه میکرد که اطلاعات در حال ارسال و دریافتن.

این ایده، که اطلاعات تو یک صفحه بارگذاری بشن و کاربر مدام مجبور به عوض کردن صفحه‌ها نشه (مگر در مواقع لازم) باعث ایجاد تعریف جدیدی از وب‌اَپلیکِیشِن‌ها شد، اِس‌پی‌اِی (SPA) یا Single Page Application راهش رو به لغت‌نامه‌ی برنامه‌نویس‌ها باز کرد!

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

اما داستان سمت برنامه‌نویس کمی فرق می‌کنه. کاربر زمانی میتونه اطلاعات رو تو مرورگرش ببینه، که کد‌های HTML ساخته شده باشن، و این یعنی برنامه‌نویس باید با هر درخواست کاربر، یا کلا صفحه رو از اول بارگذاری کنه (که دیگه SPA نیست) یا اینکه اون قسمتی از سایت رو که مرتبط با کاربر هست یا نیاز به تغییر داره رو مجددا با داده‌های جدید که از سمت سرور اومدن بارگذاری کنه.

اینجاست که سایت شما، تا حدود زیادی از سمت سرور خودش جدا میشه و مفهوم جدیدی به عنوان فرانت‌اِند (Front End) رو تعریف می‌کنه. فرانت‌اند و بَک‌اِند، در زمان‌های دور وجود نداشتن یا خیلی به هم نزدیک بودن. شما یک سایت رو کامل می‌نوشتید و مثلا تو یک صفحه PHP، همزمان از HTML و JavaScript استفاده می‌کردید. هر زمان که کاربر درخواست صفحه‌ی جدیدی رو میداد، شما یک اسکریپت جدید رو بارگذاری و صفحه‌های مربوطه رو از نو بازنویسی می‌کردید.

زمانی که تعاریف فرانت‌اند و بَک‌اند ایجاد شدند، طراحی سایت شکل جدیدی به خودش گرفت. فرانت‌اند یجورایی بیشتر نماد طراحی سایت شد و بَک‌اند بیشتر نماد منطق کاری (Business Logic). کدها سمت بک‌اند نوشته می‌شن و اطلاعات رو موقع نیاز به فرانت‌اند ارسال می‌کنن. از اینجا به بعد، برنامه‌نویس فرانت نیازی نداره که نگران SQL و دستورات مربوط به ارتباط با پایگاه داده و دریافت محصولات از اون باشه، یا حتی نگران فرایند عضویت و ورود به سایت. فرانت‌اند خودش رو بیشتر با بهبود تجربه کاربری درگیر کرد. لازم هست که بگم، این یک مفهوم کلی و جداسازی کلی بک‌اند و فرانت‌اند هست. اینکه وظیفه‌ی برنامه‌نویس چی باشه، نسبت به هر پروژه قابل تغییر هست و امرو، خیلی از برنامه‌نویس‌ها به هرد شاخه بَک و فرانت تسلط دارن.

اینجا بود که مارس ۲۰۱۳، فیسبوک اولین نسخه از کتاب‌خونه ری‌اَکت رو ارائه داد…

ری‌اکت (React)

ری‌اَکت یا ری‌اَکت‌جِی‌اِس، یک کتابخونست برای ساختن روابط کاربری. ری‌اکت به برنامه‌نویس این اجازه رو میده، که بتونه وب‌اپلیکیشن‌های بزرگی رو بسازه که از اطلاعات مختلفی استفاده می‌کنن و میتونن تو یک صفحه تغییر کنن، بدون اینکه صفحه رو مجدد لود کنن (مفهوم SPA). هدف اصلی سازندگان ری‌اکت، سرعت، سادگی و مقیاس‌پذیری بوده.

ری‌اکت صرفا تلاش میکنه تا رابط کاربری رو از راه Virtual-DOM تغییر بده. اینکه این ساز.کار به چه صورتی هست رو فعلا تشریح نمیکنم اما، اگر با معماری MVC آشنا باشید، ری‌اکت اون قسمت “V” رو به خودش اختصاص میده و میتونه با بقیه کتابخونه‌های جی‌اس خودش رو وفق بده.

یکی دیگه از اَشکال ری‌اکت، ری‌اَکت‌نِیتیو هست که کمک میکنه، نرم‌افزارهای (اکثرا موبایل) نِیتیو یا سازگار با سیستم‌عامل توسط ری‌اکت ساخته بشن. ضمنا ری‌اکت از جی‌اس‌اکس (JSX) برای نمایش و ساخت المان‌ها استفاده میکنه که توضیح میدم در این مورد.

جی‌اس‌اکس (JSX)

جِی‌اِس‌اِکس، یک زبان مشابه با HTML یا XML که کمک میکنه، تیکه‌های سایت (Component) جدای از هم ساخته بشن و به شکل یک شئ جاوا‌اسکریپت در بیان.

ساده‌تر بگم، با JSX میشه ساختارهای مشابه به HTML رو ساخت. مثال:

var nav = (
    <ul id="nav">
      <li><a href="#">Home</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Clients</a></li>
      <li><a href="#">Contact Us</a></li>
    </ul>
);

اینجا، nav به عنوان یک کامپونِنت (Component) شناخته میشه. و شما میتونید جاهای مختلف برنامه، ازش استفاده کنید (یکبار بنویسید و چندبار استفاده کنید).

این استفاده از JSX یکی دیگه از ویژگی‌های ری‌اکت به حساب میاد! اینکه شما برنامتون رو به تیکه‌های کوچکتری تقسیم می‌کنید و میتونید در جاهای مختلفی ازش استفاده کنید.

کمی بیشتر در مورد ری‌اکت

دنیای ری‌اکت جالبه. فرض کنید که یک کامپوننت رو ساختیم، مثلا یه لیست از محصولات. بعد کاربر روی یکی از محصولات کلیک میکنه، صفحه محصول باز میشه و کاربر محصول رو میبینه. کاری که ری‌اکت انجام داده چیه؟ ری‌اکت متوجه میشه که کدوم قسمت‌های سایت باید تغییر کنن و فقط اونها رو تغییر میده! پس اول شناسایی میکنه که چه چیزی تغییر کرده، و بعد تغییرات رو اعمال میکنه. ری‌اکت، به جای اینکه کل صفحه رو از اول بازسازی کنه، فقط قسمت‌هایی که تغییر کردند یا به نوعی وضعیتشون تغییر کرده رو با داده‌های جدید پر می‌کنه و اونها رو از نو میسازه و خیلی هم سریع این کار رو انجام میده!

نصب و شروع کار با ری‌اکت

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

نصاب ری‌اکت

نصاب ری‌اکت، که توسط فیسبوک ساخته شده، تو مخازن npm موجوده و میتونید اون رو به صورت عمومی توی سیستم‌عاملتو بریزید:

$ npm install -g create-react-app

\# ساخت نرم‌افزار
$ create-react-app my-app-name

\# نصب وابسته‌ها (Dependencies)
$ cd my-app-name
$ npm install

مطمئن باشید که حتما npm رو روی کامپیوترتون نصب کردید. یکی از مهم‌ترین ویژگی‌های create-react-app اینه که تمام ابزارهای لازم رو از قبل برای شما فراهم کرده. مثلا از قبل Babel برای شما نصب شده و نیازی نیست که خودتون رو درگیر نصب و راه‌اندازیش بکنید. حالا میریم سراغ یک توضیح در مورد ساختار این کتابخونه.

ساختار پوشه‌ها

اگر داخل پوشه public رو ببینید، متوجه حضور فایل index.html میشید. این فایل در حقیقت نقطه شروع برنامست و حتما باید وجود داشته باشه، این یکی از بایدهای برنامه‌های ساخته شده با create-react-app هست. یه نگاهی به داخل این فایل میندازیم:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <!--
      manifest.json provides metadata used when your web app is added to the
      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the \`public\` folder during the build.
      Only files inside the \`public\` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running \`npm run build\`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run \`npm start\` or \`yarn start\`.
      To create a production bundle, use \`npm run build\` or \`yarn build\`.
    -->
  </body>
</html>

همینطور که میبینید، این فایل چیز خاصی رو داخل خودش نداره. به جز خط ۲۸ام. که اِلِمانی تعریف شده با آی‌دی root، این رو تا اینجا توی ذهنتون داشته باشید. اتفاقی که از اینجا به بعد رخ میده، توی فایل src/index.js قرار داره. بذارید یه نگاهی هم به این فایل بندازیم:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();

این فایل هم یکی از فایل‌هایی هست که حتما باید وجود داشته باشه. دلیلش اینه که تو تتظیمات create-react-app این دو فایل به عنوان نقاط شروع برنامه تعریف شدند. خود create-react-app این دو فایل رو به هم متصل میکنه و زمانی رو که شما لازم هست خودتون بذارید تا تنظیمات رو انجام بدید، براتون ذخیره می‌کنه. میریم سراغ بررسی این فایل:

  1. اول از همه، این کد با استاندارد ای‌اس۶ نوشته شده. اگر قرار بود از استاندارد ای‌اس۵ (یا به اصطلاح Common JS) پیروی کنیم، باید مینوشتیم: var React = require("react"); تا کلاس ری‌اکت رو به پروژه اضافه کنیم. بعدا به این خواهیم پرداخت که این خط چه کارهایی انجام میده. فعلا بریم سراغ خط بعد.
  2. تو خط دوم، شئیی به نام ReactDOM فراخوانی شده. برای اطلاعاتون، قبل از نسخه ۰.۱۴ ری‌اکت، کتابخونه‌های react و react-dom یکی بودن. تنها وظیفه‌ای که ReactDOM به عهده داره، اینه که با اِی‌پی‌آی‌های render یا ReactDOM.render یک اِلِمان جِی‌اِس‌اِکس رو، داخل یک اِلِمان دیگه (اینجا document.getElementById('root')) نمایش بده.
  3. خط سوم، خیلی ساده اِستایل‌های موجود در فایل index.css رو، برای تمام اِلِمان‌هایی که اینجا قرار هست رِندِر بشن، اعمال میکنه.
  4. خط چهارم یک کامپوننت رو، اینجا به اسم app، از یک کلاس ری‌اکت فراخوانی کرد و بعد تو خط هفتم، به ReactDOM گفت که این کامپوننت رو داخل اِلِمان root رِندِر کنه. اما بریم سراغ کلاس ری‌اکت و ببینیم اصلا حرفش چی هست.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

خب، طبق معمول خط اول، کلاس‌های ری‌اکت (React) و کامپوننت (Component) رو از کتابخونه ری‌اکت فراخوانی کرده. اما تفاوت براکت‌ها {} برای کامپوننت و ری‌اکت که براکت نداره چیه؟ این یکی از ویژگی‌های ای‌اس۶ هست که به این صورته:

فرض کنید کلاسی رو به شکل زیر تعریف کردیم:

class YeClassJadid1 {/*...*/}

export class YeClassJadid2 {/*...*/}

export default class YeClassJadid3 {/*...*/}

export default class YeClassJadid4 extends YeClassDige {/*...*/}

اشیائی که تعریف شدن همشون ویژگی کلاس بودن رو دارن، اما تفاوتشون به این صورته که، کلاس اول (YeClassJadid1)، فقط تو همون فایلی که تعریف شده، یا اسکوپی (Scope) که تعریف شده قابل خونده شدنه و جاهای دیگه قابل استفاده نیست. کلاس دوم (YeClassJadid2) رو شما میتونید جاهای دیگه، با استفاده از روش import { YeClassJadid2 } from "path/to/the/file" فراخوانی کنید. دلیلش هم اینه که کلاس دوم، به طور پیش‌فرض کلاس اصلی این ماژول یا فایل نیست. اما، کلاس سوم YeClassJadid3 به طور پیش‌فرض، کلاس اصلی تعریف شده (default) و زمانی که بخواد فراخوانی بشه، میتونه به صورت import YeClassJadid3 from "path/to/the/file" یا حتی import YeEsmeDige from "path/to/the/file" فراخوانی بشه. و در نهایت هم، کلاس چهارم YeClassJadid4 مثل کلاس‌های قبلی، اما با ارث‌بری از کلاس YeClassDige خارج یا اِکسپورت میشه.

نکته‌ای که گفتنش حائز اهمیته، اینه که هر ماژول، یا هر فایل، فقط یک کلاس رو میتونه به صورت default خارج کنه! تو مثال بالا، یکی از کلاس‌های YeClassJadid3 یا YeClassJadid4 میتونن خاصیت default رو داشته باشن!

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

ساخت کامپوننت

برای اینکه بهتر متوجه بشید کامپوننت‌ها چی هستند، صفحات وب رو به تیکه‌های کوچیک تقسیم کنید. مثلا نوار بالای صفحه یه کامپوننت جدا، لیست محصولات یه کامپوننت جدا و همینطور ادامه بدید…

📷

اول، کل صفحه یک کامپوننت هست، بعد هر تیکه‌ای از صفحه تقسیم به کامپوننت‌های کوچک‌تر میشه و پیش میره. این موضوع رو با ساخت یک کامپوننت راحتت درک میکنید. برای شروع، داخل پوشه src یک پوشه دیگه به اسم components یا هر اسم دیگه‌ای که دوست دارید ایجاد کنید. اینکه پوشه‌ها چطور باشن، دست خودتونه، اما یادتون باشه که یک‌سری استاندارد یا کانوِنشِن (Convention) برای اینکار هست که مدیریت کد رو راحتتر میکنه. در نهایت داخل این پوشه، یک فایل به اسم MyComponent.js بسازید و داخلش این کد رو قرار بدید:

import React from "react";

export default class ThisIsAComponent extends React.Component {
    render() {
        return (
            <ul>
                <li>Item 1</li>
                <li>Item 2</li>
                <li>Item 3</li>
                <li>Item 4</li>
                <li>Item 5</li>
            </ul>
        );
    }
}

کامپوننتی که ساختیم خیلی سادست، یه لیست با ۵تا آیتم. که باید، حتما داخل تابع render قرار بگیرن، و این تابع هم، فقط باید یک اِلِمان رو، یا چند اِلِمانی که داخل یک المان والد جمع شدند رو برگردونه. حالا باید این کامپوننت رو داخل App.js فراخوانی کنیم. کار خیلی ساده‌ایه. App.js رو باز کنید و اون رو به شکل زیر تغییر بدید:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import ThisIsAComponent from "./components/MyComponent"

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
        
        <ThisIsAComponent/>
      </div>
    );
  }
}

export default App;

خب، کامپوننت رو ساختیم و اون رو به کامپوننت اصلی اضافه کردیم. حالا دستور npm start رو اجرا کنید و بعد داخل مرورگر به آدرس localhost:3000 برید و نتیجه رو ببینید. بعد از اینکه تموم شد، میتونید سرور رو ببندید و برید سراغ مرحله بعد.

گریزی به state

فرض کنید که قرار بود دکمه‌ای وجود داشته باشه، تا با کلیک کردن روش، کامپوننت ما یا حتی یک بخشی از کامپوننت نمایش داده بشه و یا مخفی بشه، یکی از این دو حالت ساده! اینجا لازم هست که کمی در مورد مفهوم استِیت (state) توضیح بدم. کامپوننت‌های شما، همینطور که تا اینجا دیدید، کار خاصی رو به خودی خودشون انجام نمیدن، چون در حقیقت اِلِمانهای HTML هستند، یا حتی شاید لازم باشه در طول زمان تغییراتی رو پیدا کنن. ملموس‌ترین نوع تغییر، زمانی رخ میده که شما اطلاعاتی رو از جایی (از یک سرور) دریافت میکنید و می‌خواید به کاربر نشون بدید، اما تو مدت زمانی که اطلاعات در حال دریافت هستند، علاقه دارید تا یک اسپینر (spinner) رو نمایش بدید، تا به کاربر بگید که اطلاعات در حال بارگزاری هستن. یه سناریوی دیگه اینکه، با کلیک روی یک چِک‌باکس (checkbox) یک فیلد مخفی شده رو نمایش بدید و مثال‌های دیگه. اینجاست که استیت به کمک شما میاد.

استیت در حقیقت محل ذخیره‌ي آخرین تغییرات و حالات کامپوننته. مثلا شما بهش میگید که کامپوننت من در حالت عادی، نمایش داده میشه، اما من میخوام، زمانی که کاربر روی یک دکمه کلیک میکنه، کامپوننتم رو مخفی کنم. اینجا، نمایش داده شدن یا مخفی شدن، جز حالات یا استیت کامپوننت به حساب میاد. ری‌اکت، زمانی که استیت تغییر میکنه، کامپوننت رو مجدد بارگزاری یا رِندِر میکنه.

برای اینکه با استیت هم کار کنیم و بهتر درکش کنیم، به تریتیب زیر، به کامپوننت ThisIsAComponent استیت میدیم،

  1. حالت دیفالت یا اولیه رو برای کامپوننت تعریف میکنیم
  2. توسط یک ایونت (Event) حالت یا همون استیت رو تغییر میدیم.

کامپوننت رو به شکل زیر بازنویسی میکنیم:

import React from "react";

export default class ThisIsAComponent extends React.Component {
    constructor() {
        super()

        this.state = {
            isHidden: false
        }
    }

    toggleState() {
        this.setState({
            isHidden: !this.state.isHidden
        })
    }

    render() {
        return (
            <div>
                <button onClick={this.toggleState.bind(this)}>Change</button>
                <ul hidden={this.state.isHidden}>
                    <li>Item 1</li>
                    <li>Item 2</li>
                    <li>Item 3</li>
                    <li>Item 4</li>
                    <li>Item 5</li>
                </ul>
            </div>
        );
    }
}

کارهایی که انجام شده، به همراه توضیحاتشون به ترتیب زیر هستن:

  1. constructor به کامپوننت اضافه شده. این تابع که در حقیقت از تعاریف کلاس در جاوا‌اسکریپت برگرفته شده، کارهای متفاوتی رو میتونه انجام بده. اما یادتون باشه، هر موقع که میدونستید کامپوننتتون قرار هست حالات مختلفی رو برای نمایش داشته باشه، حالت اولیش (Initial State) رو اینجا تعریف کنید. مهمترین نکته اینه که به محض تعریف کردن constructor، تابع super() فراخوانی بشه. اگر فراموش کنید که super() رو بلافاصله فراخوانی کنید، this که در حقیقت همون کامپوننت شماست و اطلاعات کامپوننت رو توی خودش داره، خالی خواهد موند و در نتیجه امکان استفاده از اِستِیت و بقیه ویژگی کامپوننت‌ها رو نخواهید داشت.
  2. قدم بعدی، داخل construct و زیر super()، استیت اولیه یا همون Default رو تعریف کردم. شکل تعریفش هم یه شئ ساده بوده که داخلش فقط از isHidden: false استفاده کردم تا بگم، در حالت عادی نمایش داده نمیشه (اینکه چطور و چه چیزی از این حالت استفاده میکنه و تغییر میکنه رو پایین‌تر توضیح میدم)
  3. یک تابعی رو تعریف کردم با اسم دلخواه toggleState که قرار هست موقع کلیک کردن دکمه، فراخوانی بشه. کاری هم که انجام میده، خیلی ساده، استیت رو تغییر میده. کد نوشته شده شاید شما رو یکم سردرگم کنه. تعریف کد به این شکله: isHidden: !this.state.isHidden و یعنی، مقدار جدید isHidden برابر خواهد بود با هر آنچه که this.state.isHidden بوده، اما چون یک ! هم اولش آوردم، یعنی اون مقدار رو بر عکسش کن. پس اگر this.state.isHidden برابر با false بود، مقدار جدیدش برابر با true میشه و برعکس.
  4. چون یک دکمه هم به کامپوننت اضافه کردم، باید کل اِلِمان‌ها رو داخل یک اِلِمان اصلی و والد جا بدم. برای همین هم، تمام المان‌ها رو داخل یک div گذاشتم.
  5. زمانی که کاربر روی دکمه کلیک میکنه، رویداد (Event) onClick اتفاق میوفته. اینجا بهش گفتم، زمانی که این رویداد اتفاق افتاد، تابع toggleState رو صدا بزنه. ضمنا از bind هم استفاده کردم، چون دکمه‌ها در حالت عادی، رویدادها رو انجام نمیدن و بایند (Bind) موظف هست تا حالت اصلی شئی که بهش پاس داده میشه رو حفظ و برای تابع مربوطه ارسالش کنه. در این مورد بعدا بیشتر توضیح میدم.
  6. و در نهایت، به المان ul گفتم، تا مخفی بودن یا نبودنش رو از this.state.isHidden بگیره. حالا، هر موقع که isHidden تغییر کنه، المان ul مجددا رِندِر میشه.

برنامه رو تست کنید و ببینید که کارکردش چطوره. کارتون که تموم شد، میریم تا یکم دیگه با تئوری دست و پنجه نرم کنیم.

درک مفاهیم ری‌اکت

مهم‌ترین تفاوت کتاب‌خونه ری‌اکت با فرِیم‌وُرک‌هایی مثل انگولار، اینه که ری‌اکت فقط برای فرانت‌اند ساخته شده. در مورد ری‌اکت، چیزهای خیلی زیادی برای گفتن وجود داره. اما دوتا از ویژگی‌های اصلی ری‌اکت، داشتن مفاهیمی مثل Properties یا به طور خلاصه Prop و همچنین State هست که قبل‌تر کمی با مفهوم State آشنا شدیم. اینجا تصمیم دارم در مورد این دو و همچنین چرخه زندگی کامپوننت‌ها بیشتر توضیح بدم.

Props

پراپ‌ها در واقع راهی برای ارتباط بین کامپوننت‌ها و جز ویژگی‌های اونها هستند (هرجا که شما کلاس React.Component رو استفاده یا همون extend کنید، میتونید ازشون بهره‌ ببرید). مهم‌ترین ویژگی پراپ‌ها، اینه که از سمت کامپوننت والد به فرزند منتقل میشن و اصطلاحا uni-directional (یک‌طرفه) هستن. زمانی هم وجود داره که شما مثل استِیت، پراپ‌های اولیه رو تعریف میکنید تا کامپوننت شما با اطلاعات اولیه (و نه حالات اولیه)، شروع به کار کنه. پراپ‌ها به شما کمک میکنن تا اطلاعات رو بین کامپوننت‌ها جابجا کنید، و زمانی که کاربر بخواد مستقیما روی خود کامپوننت تغییری اعمال کنه، باید از استیت استفاده بشه. بذارید با یک مثال ساده از پراپ استفاده کنیم:

فرض کنید که توی کامپوننت ThisIsAComponent از یک تگ هدر h1 استفاده میکردیم و قرار بود مقدار داخلش رو توسط کامپوننت والد تغییر بدیم. کد جدیدمون به این شکل میشد (فقط تابع رِندِر رو گذاشتم و مابقی کد دست‌نخورده باقی مونده):

render() {
        return (
            <div>
                <h1>{this.props.title}</h1>
                <button onClick={this.toggleState.bind(this)}>Change</button>
                <ul hidden={this.state.isHidden}>
                    <li>Item 1</li>
                    <li>Item 2</li>
                    <li>Item 3</li>
                    <li>Item 4</li>
                    <li>Item 5</li>
                </ul>
            </div>
        );
    }

حالا باید مقداری رو برای title از کامپوننت والد، یعنی App در نظر بگیرم. کار آسونیه و به شکل زیر تغییری رو تو App.js ایجاد میکنم:

...
<ThisIsAComponent title={"This is a new title"}/>
...

State

پراپ‌ها نباید تغییر کنن (به اصطلاح باید Immutable باشن)، برای همین از استیت استفاده میشه. در حالت عادی، کامپوننت‌ها استیت ندارن و از این جهت اصطلاحا بهشون Stateless گفته میشه. کامپوننت‌هایی که استیت پیدا میکنن، بهشون Stateful میگن.

کاربرد استیت برای اینه که کامپوننت بتونه اطلاعاتی که در هر بازسازی دریافت میکنه رو حفظ کنه. زمانی که شما از this.setState() استفاده میکنید، وضعیت کامپوننت بروزرسانی و مجددا بازسازی میشه. تمام این فرایند بازسازی توسط ری‌اکت اتفاق می‌افته و خیلی هم سریعه.

پراپ و استیت خیلی شبیه به هم هستند و تقریبا کار مشابهی رو انجام میدن، اما برای کارهای متفاوتی ازشون استفاده میشه. این امکام وجود داره تا خیلی از کامپوننت‌های شما Stateless باشن.

چرخه زندگی کامپوننت‌ها (Component Lifecycle)

کامپوننت‌ها به شما کمک میکنن تا یو‌آی (UI) رو به تیکه‌های کوچکتر تقسیم کنید. در حالت کلی، شما کامپوننت‌ها رو به شکل کلاس‌های جاوااسکریپت تعریف میکنید:

class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

اما کامپوننت‌ها صرفا جهت نمایش ساخته نمیشن و کارهای بیشتری میشه باهاشون انجام داد. اصطلاحا هر کامپوننتی برای خودش یک چرخه‌زندگی بخصوص یا Lifecycle داره که چندتا از پر کاربردترین‌ها رو توضیح میدم:

  • constructor() که دقیقا قبل از بارگذاری کامپوننت توسط ری‌اکت خونده میشه. بهترین استفادش، تعریف state اولیه کامپوننت هست. اگر کامپوننت stateless باشه، نیازی به تعریف این تابع نیست.
  • componentWillMount() دقیقا قبل از بارگذاری خونده میشه. و قبل از تابع render() اتفاق میوفته. به همین خاطر تعریف استیت تو این تابع پیشنهاد نمیشه. این تابع سمت سرور کارهاش رو انجام میده و اصطلاحا server-side هست. (در این مورد تو بخش Isomorphism توضیح میدم)
  • componentDidMount() بعد از اینکه کامپوننت بارگذاری شد، خونده میشه. این تابع بهترین جا برای ارسال درخواست‌ها به سرور شماست و اگر استیت رو تو این تابع با استفاده از تابع this.setState() تغییر بدید، باعث میشید که باز دوباره تابع render() فراخوانی بشه
  • componentWillReceiveProps(nextProps) زمانی فراخوانی میشه، که شما از طریق کامپوننت والد، پراپ‌های کامپوننت فرزند رو تغییر بدید و بخواید استیت جدید رو بر اساس پراپ‌های جدید تنظیم کنید. برای اینکار میتونید آرگومان‌های nextProps و this.props رو با هم مقایسه کرده و تغییرات رو ایجاد کنید. (مقایسه رو حتما انجام بدید!)
  • componentWillUpdate(nextProps, nextState) قبل از بارگذاری مجدد رخ میده، اگر پراپ‌ها و استیت‌های کامپوننت تغییری کرده باشن.
  • componentDidUpdate(prevProps, prevState) بلافاصله بعد از بارگذاری مجدد اتفاق میوفته و به شما این امکان رو میده تا المان‌ها رو دستکاری کنید.
  • componentDidCatch(error, info) برای مدیریت اِرورها و خطاها تو UI استفاده میشه.
  • مطالعه بیشتر…

جمع‌بندی

پراپها استفاده میشن تا اطلاعات از کامپوننت والد به کامپوننت فرزند یا حتی داخل کامپوننت فرزند منتقل بشن. پراپ‌ها تغییرناپذیر یا به اصطلاح Immutable هستند و نباید در هر رندر تغییر کنند.

استیت برای این استفاده میشه تا اطلاعات رو تغییر بدیم، یا اطلاعات تغییر یافته رو نمایش بدیم و اصطلاحا تغییر پذیر یا Mutable هستند. مثلا کاربر چیزی رو در سایت سرچ میکنه و بلافاصله زیرش تعدادی از نزدیک‌ترین نتایج جست‌وجو نمایش داده میشه.

مفهوم Isomorphic یا Universal

زمانهای قدیم، قبل از موقعی که NodeJS بین برنامه‌نویس‌ها محبوب بشه، جاوااسکریپت زبانی بود که باهاش تغییراتی رو تو صفحه‌های وب ایجاد میکردن تا صفحه‌ها از حالت مرده و استاتیک خارج بشن. تا اون موقع زبان جاوااسکریپت، زبانی بود که تو مرورگر کاربر اجرا میشد و به اصطلاح Client Side یا سمت کاربر بود. بعد از ظهور NodeJS جاوااسکریپت این قابلیت رو پیدا کرد که تو محیط سمت سرور هم اجرا بشه و بعد از اون جاوااسکریپت به یک زبان Server Side هم تبدیل شد.

ایزومورفیزم (Isomorphism) از ریاضیات گرفته شده و «هم‌سان» معنی میشه. چون واژه ایزومورفیک برای برنامه‌نویس‌ها کمی مشکل‌ساز میتونه باشه، به جاش از واژه یونیورسال (Universal) هم استفاده میکنن.

ایزوموفیک در شاخه وب، به معنی ساخته شدن صفحه سمت سرور یا سمت کاربر هست و در حالت کلی به NodeJS و خود JavaScript اشاره میکنه. برای اینکه تعریف رو ساده‌تر کنم، اینطور در نظر بگیرید که، کدی که سمت سرور ساخته شده، به صورت HTML به کاربر ارسال میشه و کاربر میتونه تو کد صفحهش اون رو ببینه. اما کدی که سمت کاربر ساخته میشه، تو کدهای صفحه قابل مشاهده نیست. اینجا لازم هست که به این نکته اشاره کنم، که منظور از سرور، الزاما بک‌اند نیست، و اونچه که مد نظر هست، ابزاریه که وظیفه تبادل ارتباطات رو به عهده داره.

دلایل مختلفی وجود دارن که برنامه‌نویس‌ها به اپ‌های ایزومورف علاقه‌مندند:

  • بهبود سئو،
  • پرفورمنس بهتر،
  • و نگهداری راحت‌تر.

یکی از مضوعاتی که خیلی در این مورد مهم هست، اینه که کد‌های ایزومورفیک که سمت کاربر ساخته شدن، توسط موتورهای جست‌وجو به خوبی خونده نمیشن، برای همین هم اپ‌های SPA معمولا با این ساختار دچار مشکل میشن و لازم هست تا کدشون سمت سرور ساخته بشه. (سرور رو الزاما با کامپیوتر سرور اشتباه نگیرید!)

Promise و درک ناهم‌گام‌سازی (Asynchronous)

بیاید با هم یه دنیای جالبی رو تجسم کنیم، تو این دنیای ما، هنوز گوگل وجود نداره و شما رئیس یک شرکت «پاسخ به سوالات» هستید. نحوه کار به این شکله که کاربر سوال خودش رو بسته‌بندی میکنه (Data Package) و اون رو به یک پست‌چی میده، پست‌چی این بسته رو میاره برای شرکت شما (Request) و شما بسته رو باز میکنید، به سوال جواب میدید و اون رو به پست‌چی میدید و ایشون هم برای کاربر جواب رو میبره (Response). پروسه تا اینجا واضحه فقط چند شرط وجود داره:

از اونجایی که شرکت ما خیلی خاصه :دی،

  1. کاربر زمانی که بسته رو به پست‌چی میده، باید دم در خونه صبر کنه تا پست‌چی برگرده (و از کار و زندگیش هم می‌افته)
  2. پستچی تا زمانی که شما پاسخ رو بسته‌بندی نکردید و بهش تحویل ندادید پیش شما میمونه.

این روش ارتباط، روش هم‌گام یا اصطلاحا Synchronous نام داره و همون روش قدیمیه کار با اینترنته، کد PHP مینوشتیم و با هربار کلیک رو دکمه، کاربر باید منتظر صفحه جدید میموند و نمیتونست کارهای دیگه توی سایت رو انجام بده.

حالا شرکتمون رو کمی پیشرفته‌تر میکنیم، فرایند همون شکل قبلی رو داره اما:

  1. زمانی که کاربر درخواستش رو بسته‌بندی کرد و به پست‌چی داد، برمی‌گرده خونش و کارهاش رو انجام میده
  2. شما فقط یک پست‌چی ندارید و پست‌چی‌ها میتونن از کاربر درخواست‌های مختلفی رو بگیرن و برای شما بیارن

این نوع ارتباط، ارتباط نا‌هم‌گام یا Asynchronous نام داره و خیلی تو وب‌اپ‌های SPA پر کاربرده و AJAX یکی از معروف‌ترین تکنیک‌ها برای برقراری این نوع ارتباطهاست. تو نسخه‌های جدید جی‌اس، برای اینکه کاربر رو منتظر نذاریم بعد از اینکه درخواستش رو ارسال کرد، بهش قول میدیم که در آینده جوابی رو برای درخواستش ارسال میکنیم، و کاربر میتونه به کارش ادامه بده و ماهم پردازشمون رو انجام میدیم. اینجاست که پرامِس (پرامیس؟) یا همون Promiseها خودشون رو نشون میدن.

قبل از معرفی شدن پرامس‌ها، تو جاوااسکریپت از Callbackها استفاده میشد، کال‌بک‌ها توابعی بودن که مثلا به عنوان آرگومان یک تابع دیگه تعریف میشدن، تا هنگام بُروز یک رویداد خاص (Event) کار خاصی رو هم انجام بدن. مشکل اینجا بود که ایده‌ی کال‌بک‌ها، هرچقدر هم که قشنگ بوده، تو پردازش‌های پیچیده مدیریت کد رو خیلی سخت میکرده و به اصطلاح، برنامه‌نویس رو وارد جهنم کالبَک‌ها یا همون Callback-Hell میکرده. برای همین پرامس‌ها ساخته شدند تا جایگزینی برای کال‌بک‌ها باشن.

پرامس‌ها یا جوابی رو برمیگردونن یا اینکه دلیلی رو برای عدم انجام موفقیت‌آمیز اون فرایند ارائه میدن. از اینجا میشه گفت که پرامس‌ها ساختار مشابهی مثل try/catch دارن و از همه مهمتر، سه وضعیت کلی رو شامل میشن:

  1. Pending: که یعنی در حال انجام کاری هستند،
  2. fulfilled: که یعنی کارشون رو به خوبی انجام دادن و
  3. rejected: که یعنی از پس کاری که بهشون داده شده بر نیومدن.

یه مثال ساده برای اینکه با نمونه‌ای از یک پرامس آشنا بشید به این شکله:

fetch(url)
  .then(process)
  .then(save)
  .catch(handleErrors)
;

اینجا، تابع process صبر میکنه تا کار تابع fetch تموم بشه، بعد تابع save منتظر process میمونه و اگر هرکدوم از این توابع جایی به مشکل خوردند، تابه handleErrors وظیفش رو انجام میده.

اگر هرکدوم از این توابع، پرامس باشن، میتونن ساختار مشابهی رو برای خودشون بگیرن، در واقع این قابلیت رو به شما میدن تا بتونید پرامس‌ها رو تودرتو کنید.

استفاده از fetch

حالا که متوجه مفهوم ایزومورفیک و همچنین درخواست‌های نا‌همگام شدید، بریم تا با یه مثال تو پروژمون به درک بیشتری ازشون برسیم.

قدم اول، نصب کتابخونه isomorphic-fetch از مخازن npm هست. fetch یکی از کتابخونه‌های مورد علاقه‌ی من که در کنارش کتابخونه axios وجود داره. تفاوت عمده این دو کتابخونه، تو پردازش اطلاعات هست و اکسیوس برای ای‌اس۶ آمادگی بیشتری داره، منتهی من طبق عادت پیش میرم و از فِچ (fetch) استفاده میکنم.

$ npm install --save isomorphic-fetch

کتابخونه رو نصب کنید، اینکه واژه ایزومورفیک اولش استفاده شده، نشون میده که این کتابخونه رو، هم میشه سمت سرور و هم سمت کاربر استفاده کرد. حالا، کامپوننت ThisIsAComponent رو یکمی تغییر میدیم. در نهایت کدمون باید به شکل زیر بشه:

import React from "react";
import fetch from "isomorphic-fetch";

export default class ThisIsAComponent extends React.Component {
    constructor() {
        super()

        this.state = {
            done: true,
            items: \[\]
        }
    }

    fetchData() {
        this.setState({
            done: false
        });

        fetch('http://jsonplaceholder.typicode.com/posts')
        .then(data => {
            data.json()
            .then(res => {
                this.setState({
                    done: true,
                    items: res
                })
            })
        })
        .catch(error => {
            console.log(error)
        })
    }

    render() {
        return (
            <div>
                <h1>{this.props.title}</h1>
                <button onClick={this.fetchData.bind(this)}>Get Data</button>
                <p hidden={this.state.done}>Loading</p>
                <div>
                    {
                        this.state.items.map(item => {
                            return (
                                <p>{item.title}</p>
                            )
                        })
                    }
                </div>
            </div>
        );
    }
}

خط به خط بریم جلو ببینیم چه اتفاقی افتاده:

  1. اول، از کتابخونه isomorphic-fetch شئ fetch رو فراخوانی کردم.
  2. تو تابع constructor، استیت اولیه رو تغییر دادم و به جای isHidden که اول داشتیم، done رو، که وظیفه نگهداری از وضعیت بارگذاری آیتم‌ها رو به عهده داره و items که نگهدارنده آیتم‌های دریافت شده از سرور هستند رو ساختم.
  3. تابع toggleState رو پاک کردم و به جاش از fetchData استفاده کردم. اسمش رو هم خودم انتخاب کردم. داخل این تابع اتفاقات جالبی میوفته.
    زمانی که این تابع خونده میشه (یا در واقع رو دکمه‌ای کلیک میشه که باید این تابع رو اجرا کنه) وضعیت done به false تغییر پیدا میکنه. چون در حقیقت آیتمی دریافت و کار ما هم تموم نشده.
    قدم بعدی، از fetch استفاده کردم تا از یک آدرس پیش‌فرض، یک‌سری اطلاعات الکی رو دریافت کنم. نکته مهم اینه که fetch یک پرامس هست و وضعیتش رو میشه کنترل کرد. برای همین، تو خط پایینش گفتم، هر موقع که دریافت اطلاعات تموم شد، از اطلاعات دریافت شده که اسمشون رو data گذاشتم استفاده کن و…
  4. یکی از مهم‌ترین تفاوت‌های fetch و axios تو این مرحلست، پاسخی که fetch به شما برمیگردونه، یک پاسخ خام هست و باید تبدیل به دیتای قابل خوندن بشه. fetch این کار رو با برگردوندن یک پرامس انجام میده (برای نوشتن توابع از Arrow Functionها استفاده کردم). پس گفتم، زمانی که جواب رو از سرور گرفتی، اون رو به json تبدیل کن (که خود تابع json یک پرامس برمیگردونه) و بعد از اینکه عمل تبدیل به json درست انجام شد، از حاصلش استفاده کن تا دوباره استیت رو تغییر بدی.
    done رو هم برابر با true کردم تا نشون بدم عمل دریافت اطلاعات کامل شده. در نهایت نتیجه حاصل از دریافت اطلاعات، یا همون آیتم‌های مورد نظرم رو، که حالا تبدیل به json شدند، داخل items استیت ذخیره کردم.
    اینکار باعث میشه که ری‌اکت، یکبار دیگه کامپوننت‌هایی که از این استیت استفاده میکنن رو بارگذاری کنه. در نهایت هم بررسی میکنه که آیا اشکالی وجود داشته تو کل این فرایند یا نه.
  5. آخر سر، تو تابع render دکمه Get Data رو به تابع fetchData وصل کردم. یک تگ p هم اضافه کردم که بهش گفتم، وضعیت hidden بودنش رو از done موجود تو استیت بگیره. (هر موقع که بارگذاری تموم شده بود، این المان محو میشه و هرموقع بارگذاری در حال انجام بود، این المان نمایش داده میشه)
  6. داخل المان div که به جای ul نشسته، با استفاده از تابع map() که شکل جدیدی از forEach هست، آیتم‌ها رو نمایش دادم. (پایین‌تر درمورد map توضیحات بیشتری میدم)

ادامه مطلب در سایت خودم…