ابزار Context توی React ابزار قدرتمندیه، ولی اگر بدون دقت استفاده بشه میتونه به یکی از بزرگترین دلایل افت Performance تبدیل بشه. مخصوصاً وقتی stateهایی داریم که زیاد تغییر میکنن (مثلاً هر ثانیه). توی این مطلب، مهمترین نکات عملی برای بهینهسازی Context رو بهصورت مرحلهبهمرحله مرور میکنیم.
مهمترین نکتهای که خیلی وقت ها ممکنه نادیده گرفته میشه اینه که:
با هر تغییر value در Context، تمام کامپوننتهایی که از اون Context استفاده میکنن re-render میشن.
حتی اگر اون کامپوننت، اون state ای که تغییر کرده را استفاده نکرده باشه و بهش مربوط نباشه. بصورت کلی اگر کامپوننتی هوک useContext رو در خودش فراخوانی کنه، با تغییر هر state درون context، دوباره رندر میشه که این رندر غیرضروری و اضافی هست و باعث کاهش کارایی برنامه میشه.
با توجه به این مساله، Context اصلاً برای stateهایی که زیاد و با فرکانس بالا تغییر میکنن (مثل time، scroll، mouse position) گزینهی خوبی نیست.
ولی با این روش هایی که تو ادامه بررسی می کنیم میشه این re-render های اضافی و غیرضروری رو حذف کرد.
منظور از Action ها همون توابع setState ای هستن که تو کانتکست وجود دارن. اما هدف از این جداسازی، اینه که:
کامپوننتهایی که فقط از این action ها استفاده کردن (و state ای رو set کردن)، با تغییر state دوباره render نمیشن.
درخت رندرها خیلی کوچیکتر میشه(یعنی کامپوننت هایی که دوباره رندر میشن، کمتر میشن)
نحوه پیاده سازی به این شکله:
StateContext -> data ActionsContext -> setData, updateSomething
یک Context برای state
یک Context جدا برای actions / setters
<StateContext.Provider value={states}> <ActionsContext.Provider value={actions}> {children} </ActionsContext.Provider> </StateContext.Provider>
وقتی کانتکست رو این شکلی می نویسیم:
<MyContext.Provider value={{ state, setState }}>
در هر render، یک آبجکت جدید ساخته میشه ❌
و React فکر میکنه Context تغییر کرده. درحالیکه مقادیر داخل value ممکنه تغییری نداشته باشن. در نتیجه یه re-render اضافی برای تمام کامپوننت های تحت پوشش خودش اعمال می کنه.
پس باید اون رو داخل useMemo قرار بدیم:
const value = useMemo( () => ({ state, setState }), [state] );
Stateهایی مثل:
time (هر ۱ ثانیه)
websocket data
animation progress
❌ نباید داخل Context باشن. چون هربار که با فرکانس بالا تغییر می کنن؛ re-render های زیادی رو به اپ تحمیل می کنن. (کلا کانتکست برای چنین متغیرهایی طراحی نشده.)
راهحل بهتر:
useSyncExternalStore
local state
یا حتی event-based pattern
قراردادن در ref
با توجه به مساله ی re-rendering کانتکست که بالاتر بررسی کردیم؛ بهتره کانتکست ها رو بر مبنای ویژگی(feature-based) از هم جدا کنیم(یا بر هر مبنای دیگه ای که مناسب دیدیم).
این کار باعث میشه کامپوننت هایی که با بعضی state ها تو کانتکست ارتباطی ندارن؛ با تغییر این state ها بطور بیهوده re-render نشن و اینطوری re-render اضافی حذف میشه.
UserContext
ThemeContext
AuthContext
یه پکیجی به نام use-context-selector هست که امکان انتخاب بخش مشخصی از Context را فراهم میکنه.
در این حالت، کامپوننت فقط زمانی re-render میشه که همان state یا مقداری که انتخاب کرده تغییر کنه، نه با هر تغییر در کل Context.
به این ترتیب، مشکل re-renderهای اضافی که معمولاً هنگام استفادهی مستقیم از useContext بهوجود میاد، تا حد زیادی برطرف میشه.

تو React یه هوک خیلی خفن داریم به اسم useSyncExternalStore که عملاً میاد یه store برامون درست میکنه (یه چیزی شبیه Redux، ولی خیلی سبکتر).
با این هوک میتونیم stateهامون رو بین چند تا کامپوننت به اشتراک بذاریم، بدون اینکه مجبور باشیم Provider درست کنیم یا کل درخت کامپوننت ها رو بهش وصل کنیم.
نکتهی جذابش اینه که performance خیلی بالایی داره؛ چون هر کامپوننت فقط به همون بخشی از store که بهش subscribe کرده گوش میده و فقط وقتی همون مقدار عوض بشه re-render میشه.
نه اینکه با هر تغییر کوچیک، نصف اپ دوباره رندر بشه 😅
این یعنی:
کامپوننتهای بینابینی اصلاً درگیر نمیشن
re-renderهای اضافی حذف میشن
درخت render کوچیکتر و تمیزتر میشه (در واقع هرس میشه)
من خودم تو پروژههایی که state پرنوسان داشتن تجربهی خیلی خوبی باهاش داشتم و بهبود performance کاملاً محسوس بود.
7️⃣ قرار دادن consumer ها در memo:
این راه هم می تونه مکمل باشه و جلوی re-render های عمیق رو بخاطر تغییر props ها بگیره. اما باید با دقت استفاده بشه که موثر واقع بشه.
کانتکست ابزار خیلی خوب و قدرتمندیه، ولی اگه بدون دقت و برای هر چیزی ازش استفاده کنیم، خیلی راحت میتونه تبدیل بشه به گلوگاه performance اپلیکیشن.
اگه stateهایی داریم که:
زیاد تغییر میکنن
به همهی کامپوننتها ربطی ندارن
یا باعث re-renderهای زنجیرهای شدن
می تونیم با استفاده از راه هایی که بررسی کردیم؛ performance اپلیکیشن رو بهبود بدیم.
خیلی ممنون که تا اینجا همراه بودین.
اگر دوست داشتین برام تو کامنتا بنویسین که شما از چه راه هایی برای بهینه سازی کانتکست استفاده می کنین.
با آرزوی سلامتی و موفقیت برای همه ی شما عزیزان.