React Activity: رندر دیگه Effect رو تضمین نمیکنه
خلاصهٔ کاملتر
React 19.2 یه کامپوننت جدید به اسم Activity اضافه کرده که با پراپ mode کار میکنه: حالت visible یعنی همهچی عادیه، و حالت hidden یعنی React اون بخش رو با display: none قایم میکنه ولی state و گرههای DOM سر جاشون میمونن. تو این مقاله اومده که نکتهٔ اصلی Activity نگهداشتن state نیست، بلکه یه تغییر تو چرخهٔ عمره: یه کامپوننت مخفی میتونه رندر بشه ولی Effectهاش اصلاً mount نشن.
نویسنده تعریف میکنه که تو یکی از پروژههاشون یه رندر شرطی رو با Activity عوض کردن و بعدش نشتی پیدا شد. مقصر خود Activity نبود؛ یه هوک چندساله بود که وسط رندر به یه store بیرونی subscribe میکرد و unsubscribe رو گذاشته بود تو cleanup یه useEffect. سالها درست کار کرده بود چون رندر همیشه به Effect میرسید. با Activity، React میتونه زودتر رندر کنه و اون Effect هیچوقت ساخته نشه، پس subscription بیصاحب میمونه.
کمترین اصلاحش اینه که ساخت و پاکسازی منبع هر دو تو یه Effect باشن، و برای storeهای بیرونی بهتره از useSyncExternalStore استفاده بشه که مالکیت رو صریح میکنه:
useEffect(() => {
const subscription = store.subscribe(onChange);
return () => {
subscription.unsubscribe();
};
}, []);نویسنده میگه StrictMode مدتها داشته همین رو گوشزد میکرده: تو حالت توسعه رندر رو یه بار اضافه اجرا میکنه و برای Effectها یه چرخهٔ اضافهٔ setup و cleanup و دوباره setup میسازه، و همین نامتقارنبودنِ هوک رو لو میده. فقط حواست باشه اگه StrictMode رو به جای ریشهٔ اپ روی یه زیردرخت فعال کنی، React اون چرخهٔ اضافهٔ اولیه رو براش اجرا نمیکنه.
چندتا عارضهٔ دیگه هم هست. چون DOM پاک نمیشه، video و audio و iframe مخفی میتونن به کارشون ادامه بدن و باید تو useLayoutEffect دستی متوقفشون کنی. نگهداشتن state برای تبها خوبه ولی برای فرم ساخت آیتم جدید معمولاً بد، که با عوضکردن key حل میشه. تو تستهای E2E هم چون هر دو تب تو DOM میمونن، لوکیتور Playwright ممکنه چند المان پیدا کنه و تست بشکنه.
نکات کلیدی:
- Activity تو React 19.2 اومد؛ حالت hidden با display: none مخفی میکنه و state و DOM رو نگه میداره.
- کامپوننت مخفی میتونه رندر بشه ولی Effectهاش mount نمیشن، پس هر منبعی که وسط رندر ساخته بشه ممکنه هیچوقت پاک نشه.
- برای storeهای بیرونی useSyncExternalStore جای subscribe دستی رو میگیره و مالکیت منبع رو صریح میکنه.
- StrictMode باید روی ریشهٔ اپ فعال باشه، وگرنه چرخهٔ اضافهٔ Effect برای اون زیردرخت اجرا نمیشه.
- DOM مخفی زندهست: video و audio و iframe ادامه میدن و لوکیتورهای Playwright ممکنه چند المان همنام ببینن.




