پیش از آنکه کامپوننتهای شما روی صفحه نمایش داده شوند، باید توسط React پردازش و رندر شوند. درک گامبهگام این چرخه به شما کمک میکند تا دید دقیقتری نسبت به نحوه اجرای کدهایتان پیدا کنید و رفتار مرورگر را بهتر پیشبینی کنید.
برای درک این فرآیند، بیایید یک رستوران را تصور کنیم:
کامپوننتهای شما: آشپزهای درون آشپزخانه هستند که غذاها (UI) را از روی مواد اولیه (Props و State) آماده میکنند.
خودِ React: گارسونی است که سفارش مشتریان (کاربر) را میگیرد، به آشپزخانه میبرد و در نهایت غذا را روی میز میگذارد.
نمایش هرگونه تصویر و المان روی صفحه، طی یک فرآیند ۳ مرحلهای انجام میشود:
شروع و تحریک (Triggering a render): تحویل سفارش مشتری به آشپزخانه.
رندر کردن (Rendering): آمادهسازی و طبخ غذا در آشپزخانه.
ثبت در مروگر (Committing): چیدن غذا روی میز مشتری.
به طور کلی به دو دلیل یک کامپوننت وارد فاز رندر میشود:
رندر اولیه (Initial Render): زمانی که اپلیکیشن برای اولین بار لود میشود.
بهروزرسانی استیت (State Update): زمانی که وضعیت خود کامپوننت یا یکی از اجداد (والدین) آن تغییر کرده باشد.
وقتی سایت شما بالا میآید، React باید اولین رندر را کلید بزند. فریمورکهای مدرن معمولاً این کد را در پشت صحنه مدیریت میکنند، اما ریشه کار با صدا زدن متد createRoot و متصل کردن آن به یک گره واقعی در HTML (مانند دایو با آیدی root) آغاز میشود:
// index.js
import Gallery from './Gallery.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<Gallery />); // شلیک رندر اولیه برای کل کامپوننتها
پس از رندر اولیه، شما با استفاده از توابع اصلاحکننده استیت (همان توابع set) رندرهای بعدی را تحریک میکنید. آپدیت کردن استیت کامپوننت، رندر جدیدی را در صف (Queue) پردازش React قرار میدهد. (درست مثل مشتری رستوران که پس از سفارش غذا، تشنه میشود و یک فنجان چای هم سفارش میدهد!).
بعد از اینکه رندر تحریک شد، React توابع کامپوننت شما را صدا میزند تا بفهمد چه چیزی باید روی صفحه نمایش داده شود. «رندر کردن» در تعریف فنی یعنی صدا زدن توابع کامپوننتها توسط React.
در رندر اولیه: React کامپوننت ریشه (Root Component) را صدا میزند.
در رندرهای بعدی: React فقط تابع کامپوننتی را صدا میزند که تغییر استیت آن، رندر را تحریک کرده است.
این فرآیند به صورت بازگشتی (Recursive) عمل میکند؛ اگر کامپوننت آپدیت شده یک کامپوننت دیگر را درون خود رندر کند، React به سراغ فرزند میرود و اگر فرزند هم فرزندی داشته باشد، آن را هم صدا میزند. این کار تا جایی ادامه پیدا میکند که تگهای بومی مرورگر (مثل div یا img) کاملاً استخراج شوند و React دقیقاً بداند چه ساختاری مروگر نیاز دارد.
به این ساختار نگاه کنید:
// Gallery.js
export default function Gallery() {
return (
<section>
<h1>مجسمههای الهامبخش</h1>
<Image />
<Image />
</section>
);
}
function Image() {
return <img src="sculpture.jpg" alt="Sculpture" />;
}
در رندر اولیه: React ابتدا تابع Gallery() را صدا میزند. از آنجا که این تابع حاوی سه تگ <Image /> است، به نوبت تابع Image() را هم سه بار صدا میزند تا درخت JSX کامل شود. در این مرحله گرههای DOM برای تکتک تگها شبیهسازی میشوند.
در رندرهای مجدد (Re-render): React کدهای جدید را فراخوانی کرده و محاسبه میکند که کدام یک از ویژگیها (Attributes) یا متون نسبت به رندر قبلی تغییر کردهاند. React در این مرحله هیچ تغییری روی صفحه واقعی ایجاد نمیکند و فقط تفاوتها را یادداشت میکند تا در فاز Commit از آنها استفاده کند.
توابع کامپوننتها در فاز رندر باید کاملاً خالص (Pure) رفتار کنند:
ورودی یکسان، خروجی یکسان: با Props و Stateهای مشخص، کامپوننت باید همیشه یک JSX کاملاً یکسان پس بدهد. (اگر مشتری سالاد گوجه سفارش داده، نباید سالاد پیاز تحویل بگیرد!).
سرتان به کار خودتان باشد: کامپوننت در زمان رندر نباید هیچ شیء یا متغیری که قبل از رندر وجود داشته را تغییر دهد (رندر یک کامپوننت نباید استیت کامپوننت بالاسری یا متغیرهای سراسری را دستکاری کند).
در غیر این صورت، با رشد پروژه با باگهای عجیب و رفتارهای غیرقابل پیشبینی مواجه میشوید. در محیط توسعه، React با فعال بودن قابلیت Strict Mode هر کامپوننت را عمداً دو بار صدا میزند تا اگر ناخالصی (Side Effect) در کدهای رندر وجود دارد، سریعتر خود را نشان دهد.
پس از پایان رندر و محاسبه تفاوتها، نوبت به گارسون (React) میرسد تا تغییرات را روی میز (مرورگر) پیاده کند:
در رندر اولیه: React از API بومی مرورگر یعنی appendChild() استفاده میکند تا تمام گرههای ساخته شده را یکباره وارد صفحه کند.
در رندرهای مجدد: React حداقل عملیات ممکن (که در فاز رندر محاسبه شده بود) را روی DOM واقعی اعمال میکند تا ظاهر صفحه با آخرین خروجی رندر مطابقت داشته باشد.
React فقط و فقط در صورتی گرههای DOM را تغییر میدهد که تفاوتی بین دو رندر وجود داشته باشد.
به این مثال جذاب توجه کنید؛ این کامپوننت ساعت، هر ثانیه یکبار از طریق والد خود رندر مجدد میشود. اما اگر شما درون تگ <input> متنی بنویسید، با تغییر ثانیهها متن شما پاک نمیشود:
export default function Clock({ time }) {
return (
<>
<h1>{time}</h1>
<input /> {/* متن این اینپوت در رندرهای پیاپی پاک نمیشود */}
</>
);
}
این پدیده به این دلیل کار میکند که در گام Commit، ریاکت میبیند تگ <input> دقیقاً در همان جایگاه قبلی در درخت JSX قرار دارد و هیچ چیز داخلی آن تغییر نکرده است؛ بنابراین اصلاً به این تگ و مقدارش دست نمیزند و فقط متن داخل تگ <h1> را با زمان جدید بازنویسی میکند.
پس از اینکه فاز Commit به پایان رسید و React درخت DOM واقعی را بهروزرسانی کرد، مروگر متوجه تغییرات شده و صفحه را از نو رنگآمیزی میکند تا پیکسلهای جدید به چشم کاربر بیایند. این فرآیند به عنوان Browser Rendering شناخته میشود، اما در ادبیات طراحی وب به آن Painting (نقاشی) میگویند تا با فاز رندر ریاکت اشتباه گرفته نشود.
هر نوع بهروزرسانی صفحه در برنامههای React طی ۳ گام رخ میدهد: Trigger (سفارش)، Render (آشپزخانه) و Commit (چیدن روی میز).
ابزار Strict Mode با دو بار صدا زدن توابع در محیط دولوپمنت، به یافتن کدهای ناخالص و باگزا کمک میکند.
اگر نتایج رندر جدید کاملاً با رندر قبلی همخوانی داشته باشد، React هرگز به ساختار DOM واقعی مرورگر دست نمیزند و عملیات کامیت را بهینهسازی میکند.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://react.dev/learn