رندر و کامیت در React

قبل از اینکه کامپوننتهای شما روی صفحه نمایش داده شوند، باید توسط React رندر شوند. درک مراحل این فرآیند به شما کمک میکند تا دربارهی نحوهی اجرای کد خود فکر کرده و رفتار آن را توضیح دهید.
تصور کنید که کامپوننتهای شما مانند آشپزهایی در آشپزخانه هستند که از مواد اولیه، غذاهای خوشمزه درست میکنند. در این سناریو، React، پیشخدمت است که درخواستها را از مشتریان میگیرد و سفارشات آنها را میآورد. این فرآیند درخواست و خدمت UI سه مرحله دارد:
- ایجاد یک رندر (تحویل سفارش مهمان به آشپزخانه)
- رندر کردن کامپوننت (آمادهسازی سفارش در آشپزخانه)
- کامیت به DOM (قرار دادن سفارش روی میز)
مرحله ۱: ایجاد یک رندر
دو دلیل برای رندر کردن یک کامپوننت وجود دارد:
- این رندر اولیه کامپوننت است.
- وضعیت کامپوننت (یا یکی از کامپوننت های بالا سری آن) بهروز شده است.
رندر اولیه
زمانی که برنامه شما شروع میشود، باید رندر اولیه را ایجاد کنید. فریمورکها و محیطهای توسعه گاهی اوقات این کد را پنهان میکنند، اما این کار با فراخوانی تابع createRoot
و ارسال گره DOM هدف به آن و سپس فراخوانی متد render
روی خروجی تابع بهمراه کامپوننت مورد نظر انجام میشود:
مشاهده نتیجه در در CodeSandbox
import Image from './Image.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'))
root.render(<Image />);
سعی کنید فراخوانی root.render()
را کامنت کنید و ببینید که کامپوننت ناپدید میشود!
رندر دوباره هنگام بهروزرسانی وضعیت
پس از اینکه کامپوننت برای اولین بار رندر شد، میتوانید با بهروزرسانی وضعیت آن با استفاده از تابع set
، رندرهای بیشتری را ایجاد کنید. بهروزرسانی وضعیت کامپوننت بهطور خودکار یک رندر را به صف میکند. (شما میتوانید این فرآیند را اینطور تصور کنید که یک مهمان رستوران پس از ثبت سفارش اولیهاش، چای، دسر و چیزهای دیگری را سفارش میدهد، بسته به وضعیت تنشگی یا گرسنگیاش.)
مرحله ۲: React کامپوننتهای شما را رندر میکند
پس از اینکه شما یک رندر را شروع کردید، React کامپوننتهای شما را فراخوانی میکند تا بفهمد چه چیزی باید روی صفحه نمایش داده شود. "رندر کردن" به معنای این است که React کامپوننتهای شما را فراخوانی میکند.
- در رندر اولیه، React کامپوننت ریشه را فراخوانی خواهد کرد.
- برای رندرهای بعدی، React تابع کامپوننتی که بهروزرسانی وضعیت آن، رندر را ایجاد کرده است، فراخوانی خواهد کرد.
این یک فرآیند بازگشتی است: اگر کامپوننت بهروز شده، کامپوننت دیگری را برگرداند، React آن کامپوننت را در مرحله بعد رندر میکند، و اگر آن کامپوننت هم چیزی را برگرداند، React آن کامپوننت را نیز در مرحله بعد رندر میکند، و این فرآیند ادامه مییابد تا وقتی که دیگر هیچ کامپوننت تو در تویی وجود نداشته باشد و React دقیقاً بداند چه چیزی باید روی صفحه نمایش داده شود.
در مثال زیر، React چندین بار Gallery()
و Image()
را فراخوانی خواهد کرد:
مشاهده نتیجه کد در CodeSandbox
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"
/>
);
}
- در حین رندر اولیه، React گرههای DOM را برای تگ های
<section>
,<h1>
, و سه تگ<img>
ایجاد خواهد کرد. - در حین رندر دوباره، React محاسبه خواهد کرد که کدام ویژگیها، اگر ویژگی ای وجود داشته باشند، از زمان رندر قبلی تغییر کردهاند. React هیچ کاری با آن اطلاعات تا مرحله بعدی، یعنی مرحله کامیت، نمیکند.
رندر کردن همیشه باید محاسبهای خالص باشد:
- ورودهای یکسان، خروجی یکسان. با توجه به ورودیهای یکسان، یک کامپوننت باید همیشه یک JSX یکسان برگرداند. (زمانی که کسی یک سالاد با گوجهفرنگی سفارش میدهد، نباید یک سالاد با پیاز دریافت کند!)
- به امور خود میپردازد. نباید هیچ شی یا متغیری که قبل از رندر وجود داشته را تغییر دهد. (یک سفارش نباید سفارش هیچ کس دیگری را تغییر دهد.)
در غیر این صورت، ممکن است با اشکالات گیجکننده و رفتارهای غیرقابل پیشبینی در هنگام رشد پیچیدگی کد خود روبهرو شوید. وقتی در "حالت سخت گیرانه" در حال توسعه هستید، React تابع هر کامپوننت را دو بار فراخوانی میکند که میتواند به شناسایی اشتباهات ناشی از توابع غیرخالص کمک کند.
بهینهسازی عملکرد
رفتار پیشفرض رندر کردن همهی کامپوننتهای تو در توی کامپوننت بهروز شده، اگر کامپوننت جایگاه بالاتری در درخت رندر باشد، عملکرد بهینه ای نیست. اگر با یک مشکل پرفرمنس روبهرو شدید، چند راهحل برای رفع این مشکلات در مطلب عملکرد پیشنهاد شده است. زودتر از موعد بهینهسازی نکنید!
مرحله ۳: React تغییرات را به DOM تأیید میکند
پس از رندر کردن (فراخوانی) کامپوننتهای شما، React درخت DOM را تغییر می دهد.
- برای رندر اولیه، React از متد
appendChild()
استفاده میکند تا تمام گرههای DOM که ایجاد کرده است را روی صفحه نمایش دهد. - برای رندرهای دوباره، React حداقل عملیات لازم (که در حین رندر کردن محاسبه شده است!) را اعمال میکند تا DOM با آخرین خروجی رندر مطابقت پیدا کند.
React تنها درصورتی گرههای DOM را تغییر میدهد که تفاوتی بین رندرها وجود داشته باشد. به عنوان مثال، در اینجا یک کامپوننت داریم که هر ثانیه با ورودی های مختلف از سوی والدش دوباره رندر میشود. اگر مقداری متن به <input>
اضافه کنید و مقدار آن را بهروزرسانی کنید، متوجه می شوید که هنگام دوباره رندر شدن کامپوننت متن وارد شده ناپدید نمیشود:
مشاهده کد ونتیجه اجرای آن در CodeSandbox
export default function Clock({ time }) {
return (
<>
<h1>{time}</h1>
<input />
</>
);
}
این عملکرد به این دلیل است که در این مرحله آخر، React فقط محتوای <h1>
را با مقدار جدید ورودی time
بهروزرسانی میکند. React میبیند که <input>
در JSX در همان جایی که آخرین بار قرار داشت، قرار دارد. بنابراین به <input>
—یا مقدار آن—دست نمیزند!
نتیجهگیری: رنگآمیزی مرورگر
پس از اتمام رندر و بهروزرسانی DOM توسط React، مرورگر صفحه را دوباره رنگآمیزی خواهد کرد. اگرچه این فرآیند به عنوان "رندر مرورگر" شناخته میشود، اما ما برای جلوگیری از سردرگمی در سراسر مستندات، به آن "رنگآمیزی" یا "painting" میگوییم.

جمع بندی
- هر بهروزرسانی صفحه در یک برنامه React در سه مرحله انجام میشود:
- ایجاد
- رندر
- تأیید
- میتوانید از حالت سخت گیرانه استفاده کنید تا اشتباهات را در کامپوننتهای خود پیدا کنید
- React اگر نتیجه رندر همانند بار گذشته باشد به DOM دست نمیزند