Urgent و Non-Urgent Updates: جمعبندی کلی فصل
در طول این فصل، از Concurrent Rendering و Scheduler شروع کردیم و با Transition، startTransition، useTransition و useDeferredValue، ابزارهای عملی را دیدیم. در این درس آخر، همه اینها را زیر یک چارچوب واحد، یعنی تفاوت Urgent و Non-Urgent Updates، جمع میکنیم و یک راهنمای تصمیمگیری عملی میسازیم.
یادآوری تعریف این دو دسته
در درس دوم این فصل، بهطور مفهومی این دو دسته را دیدیم:
- Urgent (فوری): نتیجه مستقیم یک تعامل که کاربر انتظار واکنش بیدرنگ دارد؛ مثل تایپ در یک فیلد، یا تغییر وضعیت یک چکباکس.
- Non-Urgent (غیرفوری یا Transition): نتیجهای که مهم است نهایتاً درست باشد، اما نیازی نیست همین یک لحظه، بدون وقفه، کامل شود؛ مثل بهروزرسانی یک لیست فیلترشده بزرگ.
رفتار پیشفرض React: همهچیز Urgent است
نکته مهمی که باید از کل این فصل به یاد بماند: بهطور پیشفرض، هر فراخوانی set، فوری در نظر گرفته میشود. ابزارهایی که در این فصل دیدیم (startTransition، useTransition، useDeferredValue)، راههای صریح برای اعلام اینکه «این مورد خاص، استثناست و میتواند غیرفوری باشد» هستند؛ نه اینکه رفتار پیشفرض را تغییر دهند.
یک جدول تصمیمگیری عملی
| موقعیت | ابزار پیشنهادی |
|---|---|
| مقدار یک فیلد ورودی که باید همیشه بلافاصله روی صفحه دیده شود | setState معمولی (بدون هیچ ابزار این فصل) |
| یک بهروزرسانی که خودتان آن را آغاز میکنید (مثل تغییر تب) و محاسبه نتیجهاش سنگین است | useTransition |
| مقداری که از Props یا جای دیگری میرسد و کنترلی روی لحظه تغییرش ندارید | useDeferredValue |
| نیاز فقط به علامتگذاری یک set، بدون نیاز به نشانگر pending | startTransition مستقل |
یک اشتباه رایج: Urgent کردن همهچیز با ترس از کندی
ممکن است وسوسه شویم همه بهروزرسانیها را Transition علامت بزنیم، «برای احتیاط». اما همانطور که در فصل سیزدهم درباره Memoization دیدیم، هر ابزار بهینهسازی، هزینه و پیچیدگی خودش را دارد؛ Transition کردن یک بهروزرسانی سبک و بیاهمیت (مثل تغییر یک مقدار Boolean ساده)، هیچ فایدهای ندارد و فقط یک لایه غیرضروری اضافه میکند.
یک اشتباه رایج دیگر: فراموش کردن خود ورودی
در تمام مثالهای این فصل، نکتهای که عمدی و تکرارشونده بود: خود مقدار query در فیلد ورودی (یعنی چیزی که مستقیم با تایپ عوض میشود)، هرگز Transition نمیشد؛ فقط محاسبه سنگین مبتنیبر آن (لیست فیلترشده) بود که به تأخیر میافتاد. اگر خود فیلد ورودی را هم Transition کنیم، همان مشکل اصلی (تایپ با تأخیر دیده شود) بازمیگردد؛ این دقیقاً نقطهای است که Urgent و Non-Urgent باید بهدرستی از هم جدا شوند.
رابطه این فصل با بقیه دوره
این فصل، لایهای از React است که معمولاً فقط در برنامههای با تعاملهای سنگین و بلادرنگ (جستوجوی زنده روی دادههای بزرگ، ویرایشگرهای پیچیده) واقعاً احساس میشود. بسیاری از برنامهها، هرگز نیازی به useTransition یا useDeferredValue پیدا نمیکنند؛ همانطور که در فصل اول این دوره، درباره اولویت دادن به سادگی پیش از بهینهسازی گفتیم، این ابزارها، آخرین لایه در جعبهابزار عملکرد هستند، نه اولین قدم.
مثال قابل اجرا
در این مثال نهایی، هر دو نکته کلیدی فصل با هم دیده میشوند: فیلد ورودی، همیشه فوری است؛ فقط محاسبه سنگین، با useDeferredValue، به تأخیر افتاده است.
import { useState, useDeferredValue } from "react";
function filterLargeList(query) {
const items = Array.from({ length: 15000 }, (_, i) => "آیتم " + i);
if (!query) return items.slice(0, 15);
return items.filter((item) => item.includes(query)).slice(0, 15);
}
function App() {
const [query, setQuery] = useState(""); // Urgent: همیشه فوری
const deferredQuery = useDeferredValue(query); // Non-Urgent: میتواند عقب بماند
const results = filterLargeList(deferredQuery);
const isStale = query !== deferredQuery;
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="جستوجو..." />
<div style={{ opacity: isStale ? 0.5 : 1 }}>
<ul>
{results.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
</div>
);
}جمعبندی
بهطور پیشفرض، هر بهروزرسانی در React فوری است؛ ابزارهای این فصل، راههایی صریح برای استثنا کردن موارد خاص و کماولویت هستند، نه تغییر رفتار عمومی. انتخاب درست بین useTransition (برای بهروزرسانیهایی که خودتان آغاز میکنید) و useDeferredValue (برای مقادیر دریافتی)، و مراقبت از اینکه خود تعامل کاربر همیشه فوری بماند، خلاصه عملی کل این فصل است. با این درس، فصل بیستودوم به پایان میرسد و در ادامه به آزمون فصل ۲۲ میرویم.
