رندر و کامیت

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

You will learn

  • معنای رندر کردن در ری‌اکت
  • زمان و دلیل رندر شدن یک کامپوننت در ری‌اکت
  • مراحل درگیر در نمایش یک کامپوننت در صفحه نمایش
  • دلیل اینکه رندر کردن همیشه به به‌روزرسانی دام منجر نمی‌شود

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

  1. فراخوانی یک رندر (رساندن سفارش مشتری به اشپزخانه)
  2. رندر کردن کامپوننت (اماده کردن سفارش در اشپزخانه)
  3. رساندن به دام (گذاشتن سفارش روی میز)
  1. React as a server in a restaurant, fetching orders from the users and delivering them to the Component Kitchen.
    Trigger
  2. The Card Chef gives React a fresh Card component.
    Render
  3. React delivers the Card to the user at their table.
    Commit

Illustrated by Rachel Lee Nabors

قدم اول: فراخوانی یک رندر

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

  1. است رندر اولیه
  2. کامپوننت (یا یکی از نیاکان آن) حالت اپدیت شده است

رندر اولیه

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

import Image from './Image.js';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'))
root.render(<Image />);

با امتحان کردن کد فراخوانی root.render() را کامنت کنید و تغییرات را مشاهده کنید!

دوباره رندر شدن زمانی که حالت تغییر می‌کند

پس از اینکه کامپوننت ابتدایی رندر شده است، می‌توانید با به‌روزرسانی وضعیت آن با استفاده از تابع set رندرهای بیشتری را فراخوانی کنید. به‌روزرسانی وضعیت کامپوننت شما به‌صورت خودکار یک رندر را در صف قرار می‌دهد. (می‌توانید این را تصور کنید که یک مشتری رستوران پس از سفارش اول، به تبلیغ چای، دسر و انواع دیگر از چیزها علاقه دارد، به تبع وضعیت تشنگی یا گرسنگی خود.)

  1. React as a server in a restaurant, serving a Card UI to the user, represented as a patron with a cursor for their head. They patron expresses they want a pink card, not a black one!
    State update...
  2. React returns to the Component Kitchen and tells the Card Chef they need a pink Card.
    ...triggers...
  3. The Card Chef gives React the pink Card.
    ...render!

Illustrated by Rachel Lee Nabors

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

پس از اینکه یک رندر فراخوانی می‌شود، ری‌اکت کامپوننت‌های شما را فراخوانی می‌کند تا بفهمد چه چیزی باید در صفحه نمایش داده شود. “رندر کردن” به معنای فراخوانی React از کامپوننت‌های شما است.

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

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

در مثال زیر، ری‌اکت چند بار Gallery() و Image() را فراخوانی می‌کند:

export default function Gallery() {
  return (
    <section>
      <h1>Inspiring Sculptures</h1>
      <Image />
      <Image />
      <Image />
    </section>
  );
}

function Image() {
  return (
    <img
      src="https://i.imgur.com/ZF6s192.jpg"
      alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals"
    />
  );
}

  • در رندر اولیه، ری‌اکت گره‌های DOM برای <section>، <h1> و سه تگ <img> ایجاد می‌کند.
  • در یک رندر مجدد، ری‌اکت محاسبه می‌کند که ویژگی‌های آن‌ها، اگر هرکدام تغییر کرده باشد، از زمان رندر قبلی چه تغییراتی کرده‌اند. این اطلاعات را تا مرحلهٔ بعد، یعنی مرحلهٔ commit، استفاده نخواهد کرد.

Pitfall

رندر کردن همیشه باید یک محاسبه خالص باشد:

  • ورودی‌های یکسان، خروجی یکسان. با دادن ورودی‌های یکسان، یک کامپوننت همیشه باید به همان JSX بازگردد. (وقتی کسی یک سالاد با گوجه می‌سفارش دهد، نباید یک سالاد با پیاز دریافت کند!)
  • به کار خود توجه می‌کند. نباید هیچ‌گونه تغییری در اشیاء یا متغیرهایی که قبل از رندر وجود داشته‌اند، ایجاد کند. (یک سفارش نباید سفارش دیگری را تغییر دهد.)

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

Deep Dive

بهینه‌سازی عملکرد

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

قدم سوم: ری‌اکت تغییرات را در دام اعمال می‌کند

پس از رندر (فراخوانی) کامپوننت‌های شما، ری اکت DOM را تغییر می‌دهد.

  • برای رندر اولیه، ری اکت از API DOM appendChild() برای قرار دادن تمام گره‌های DOMی که ایجاد کرده است روی صفحه نمایش استفاده می‌کند.
  • برای رندرهای مجدد، ری اکت عملیات ضروری حداقل را (هنگام رندر!) اجرا می‌کند تا DOM با آخرین خروجی رندرینگ هماهنگ شود.

React تنها زمانی گره‌های DOM را تغییر می‌دهد که تفاوتی بین رندرها وجود داشته باشد. به عنوان مثال، در زیر یک کامپوننت است که هر ثانیه با ویژگی‌های مختلف از والدینش دوباره رندر می‌شود. توجه کنید چگونه می‌توانید متنی را به <input> اضافه کنید و مقدار آن را به‌روزرسانی کنید، اما متن وقتی که کامپوننت دوباره رندر می‌شود ناپدید نمی‌شود:

export default function Clock({ time }) {
  return (
    <>
      <h1>{time}</h1>
      <input />
    </>
  );
}

این کار به این دلیل کار می‌کند که در این مرحله آخر، React تنها محتوای <h1> را با time جدید به‌روزرسانی می‌کند. این می‌بیند که <input> در JSX در همان مکانی که قبلاً بوده است ظاهر شده است، بنابراین React به <input> - یا value آن - دست نمی‌زند!

ختام: نقاشی مرورگر

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

A browser painting 'still life with card element'.

Illustrated by Rachel Lee Nabors

Recap

  • هر به‌روزرسانی صفحه در یک اپلیکیشن React در سه مرحله انجام می‌شود:
    1. فعال کردن
    2. رندر
    3. Commit
  • می‌توانید از حالت دقیق استفاده کنید تا اشتباهات در کامپوننت‌های خود را پیدا کنید.
  • اگر نتیجه رندرینگ همانند زمان گذشته باشد، React به DOM دست نمی‌زند.