Concurrent Rendering چیست
در تمام فصلهای قبل، فرض بیچونوچرای ما این بود که وقتی React شروع به محاسبه خروجی JSX یک کامپوننت میکند، این محاسبه را تا انتها، بدون وقفه، ادامه میدهد. این فصل، دقیقاً همین فرض را به چالش میکشد: Concurrent Rendering، قابلیتی است که به React اجازه میدهد یک Render را، در میانه کار، متوقف، کنار گذاشته یا با Render دیگری جایگزین کند.
یادآوری مدل قدیمی: Render غیرقابلوقفه
در فصل یازدهم دیدیم که Render Phase، یک محاسبه است که منجر به یک درخت JSX جدید میشود. در مدل سنتی (که تا پیش از این فصل فرض میکردیم)، وقتی React این محاسبه را برای یک بهروزرسانی شروع میکند، باید آن را کامل تمام کند، پیش از اینکه به هر بهروزرسانی دیگری رسیدگی کند؛ حتی اگر در همین حین، یک بهروزرسانی مهمتر (مثل واکنش به تایپ کاربر) هم در صف باشد.
مشکل: یک Render سنگین، باعث قفل شدن رابط کاربری میشود
فرض کنید کاربری در یک فیلد جستوجو تایپ میکند و هر تغییر، باعث فیلتر شدن یک لیست بسیار بزرگ میشود. اگر فیلتر کردن و رندر دوباره این لیست، چند صد میلیثانیه طول بکشد، در مدل سنتی، کل مرورگر در این مدت، به هیچ ورودی دیگری (حتی تایپ حرف بعدی) واکنش نشان نمیدهد؛ رابط کاربری، برای کاربر، «قفلشده» و کند به نظر میرسد.
راهحل: اجازه وقفه در میانه Render
Concurrent Rendering، به React اجازه میدهد یک Render سنگین را به قطعات کوچکتر تقسیم کند و بین هر قطعه، کنترل را به مرورگر برگرداند تا اگر رویداد مهمتری (مثل تایپ) در صف است، ابتدا به آن رسیدگی شود. اگر در همین حین، دادهای که Render در حال محاسبه آن بود تغییر کند، React میتواند آن محاسبه نیمهکاره را کاملاً دور بریزد و از نو، با داده جدید، شروع کند؛ بدون اینکه کاربر هرگز نتیجهای نیمهکاره یا ناسازگار ببیند.
چرا این موضوع، یک ویژگی زیرساختی است، نه یک Hook جدید
نکته مهمی که باید از همین ابتدا روشن شود: Concurrent Rendering، خودش یک API یا Hook نیست که مستقیم فراخوانی کنیم؛ یک قابلیت زیرساختی در موتور React است که همیشه، در پسزمینه، برای همه بهروزرسانیها فعال است. ابزارهایی که در درسهای بعدی این فصل میبینیم (مانند useTransition و useDeferredValue)، راههایی هستند که به ما اجازه میدهند، بهطور صریح، به React بگوییم کدام بهروزرسانیها میتوانند وقفهپذیر و کماولویتتر در نظر گرفته شوند.
ارتباط با Batching که در فصل سوم دیدیم
در فصل سوم، با Batching آشنا شدیم: ترکیب چند تغییر State در یک رندر واحد. Concurrent Rendering، گام بزرگتری در همین مسیر است؛ اگر Batching درباره «چند تغییر را با هم انجام بده» بود، Concurrent Rendering درباره «کدام تغییر، اول و کدام، بعداً، و با قابلیت وقفه» است. هر دو، بخشی از همان تلاش کلی React برای کنترل هوشمندانهتر زمانبندی رندر هستند.
یک مثال ساده از تفاوت تجربه کاربری
بدون Concurrent Rendering، تایپ سریع در یک فیلد جستوجو، همراه با یک فیلتر سنگین، میتواند حسی «چسبنده» و با تأخیر داشته باشد؛ هر حرف، انگار دیرتر از واقعیت روی صفحه ظاهر میشود. با Concurrent Rendering (و ابزارهایی که در ادامه این فصل میبینیم)، خود فیلد ورودی، همیشه فوری و روان به نظر میرسد، حتی اگر نمایش نتایج فیلترشده، کمی عقبتر، اما بدون قفل کردن کل صفحه، برسد.
مثال قابل اجرا
در این مثال، یک محاسبه نسبتاً سنگین با هر تایپ اجرا میشود؛ هدف این مثال، نشان دادن همان مسئلهای است که Concurrent Rendering قرار است در درسهای بعدی، با ابزارهای مشخص، آن را بهتر کند.
import { useState } from "react";
function filterLargeList(query) {
const items = Array.from({ length: 20000 }, (_, i) => "آیتم " + i);
if (!query) return items.slice(0, 20);
return items.filter((item) => item.includes(query)).slice(0, 20);
}
function App() {
const [query, setQuery] = useState("");
const results = filterLargeList(query);
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="جستوجو در لیست بزرگ" />
<ul>
{results.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}جمعبندی
Concurrent Rendering، قابلیتی زیرساختی در React است که اجازه میدهد یک محاسبه Render، در میانه کار، متوقف، دور ریخته یا با محاسبهای جدیدتر جایگزین شود؛ هدف آن، جلوگیری از قفل شدن رابط کاربری بهخاطر یک بهروزرسانی سنگین است. این قابلیت، همیشه در پسزمینه فعال است، اما ابزارهای صریحی مثل useTransition، که در درس بعدی میبینیم، به ما کنترل بیشتری روی آن میدهند.
