React Compiler جای context selector رو پر میکنه
خلاصهٔ کاملتر
مقاله با یه مثال آشنا شروع میشه: یه گروه رادیویی که هر رادیو باید بدونه انتخاب شده یا نه. وقتی انتخاب عوض میشه، فقط دو تا رادیو باید دوباره رندر بشن، ولی با context همهٔ consumerها (یعنی کامپوننتهایی که context رو میخونن) رندر میشن. خیلیها برای همین دنبال یه useContextSelector بودن، ولی React هنوز چنین APIای نداره. نویسنده یادآوری میکنه که قبلاً calculateChangedBits و unstable_observedBits این کار رو میکردن، ولی تو PR شمارهٔ 20953 حذف شدن.
خیلیها الان سراغ useSyncExternalStore میرن، ولی به گفتهٔ نویسنده مستندات React اونو بیشتر پلی به state خارجیِ موجود میدونه. اون معتقده بیرون رفتن از primitiveهای React ریسک داره، چون ممکنه قابلیتهایی مثل concurrent rendering از دست بره. مثلاً تو تستهای Daishi Kato، بیشتر این کتابخونهها، از جمله store مربوط به Base UI، نمیتونن رندر رو وسط کار قطع کنن.
اول الگوی رسمی مستندات React امتحان شد: کامپوننت بیرونی context رو میخونه و checked رو حساب میکنه، بعد به یه فرزند React.memo شده prop میده. اینجوری بیرونیها رندر میشن ولی ۴۹۹۸ تا view عوضنشده کارشون رو انجام نمیدن. تو بنچمارک p95 با ۵۰۰۰ رادیو روی MacBook Pro M3 Pro، کلیک با store حدود 19.2 میلیثانیه و با context و memo حدود 18.9 میلیثانیه طول کشید.
بعد نویسنده به نظر Joseph Savona از تیم React رسید که میگه compiler خیلی از چیزی که مردم از context selector میخوان رو همین الان انجام میده. با React Compiler (یعنی ابزاری که موقع build خودش کد رو memoize میکنه) دیگه نیازی به کامپوننت جدا نیست:
function Radio({ value }) {
const context = React.useContext(RadioContext);
const checked = context.value === value;
const onSelect = context.onSelect;
return <input type="radio" name="demo" checked={checked} onChange={() => onSelect(value)} />;
}همهٔ رادیوها هنوز رندر میشن، ولی compiler برای هر رادیویی که checked، onSelect و valueش عوض نشده، همون JSX قبلی رو برمیگردونه. یعنی همون ایدهٔ memo، فقط داخل خود کامپوننت. نتیجهش کلیک 18.4 میلیثانیه و با کندی ۴ برابری CPU، 63.6 میلیثانیه بود که تو همهٔ ستونها همپای بقیه یا کمی بهتر بود. نویسنده تأکید میکنه که این بنچمارک محدوده و رتبهبندی کلی نیست.
برای کسایی که هنوز compiler ندارن، نویسنده یه helper کوچیک به اسم withContextSelector ساخته که با React.memo و یه مقایسهٔ shallow همین الگو رو تمیزتر پیاده میکنه و نیازی به build step نداره.
نکات کلیدی:
- context در React با هر تغییر همهٔ consumerها رو دوباره رندر میکنه
- calculateChangedBits تو PR شمارهٔ 20953 از React حذف شد
- با ۵۰۰۰ رادیو، کلیک با compiler حدود 18.4ms و با store حدود 19.2ms بود
- compiler برای ورودیهای عوضنشده JSX قبلی رو دوباره استفاده میکنه
- بیشتر storeهای مبتنی بر useSyncExternalStore تو تستهای concurrent rendering رد میشن
- بدون compiler هم الگوی React.memo با کامپوننت بیرونی و درونی جواب میده




