بهینهسازی عملکرد React Context با استفاده از الگوی انتخابگر. رندر مجدد و کارایی برنامه را با مثالهای عملی و بهترین شیوهها بهبود بخشید.
بهینهسازی React Context: الگوی انتخابگر و عملکرد
React Context یک مکانیسم قدرتمند برای مدیریت وضعیت برنامه و به اشتراک گذاری آن در بین اجزا بدون نیاز به prop drilling فراهم می کند. با این حال، پیادهسازیهای ساده Context میتوانند منجر به گلوگاههای عملکرد شوند، به خصوص در برنامههای بزرگ و پیچیده. هر بار که مقدار Context تغییر میکند، تمام اجزای مصرفکننده آن Context دوباره رندر میشوند، حتی اگر فقط به بخش کوچکی از دادهها وابسته باشند.
این مقاله به بررسی الگوی انتخابگر به عنوان یک استراتژی برای بهینهسازی عملکرد React Context میپردازد. ما بررسی خواهیم کرد که چگونه کار میکند، مزایای آن چیست و مثالهای عملی برای نشان دادن کاربرد آن ارائه میدهیم. ما همچنین در مورد ملاحظات مربوط به عملکرد و تکنیکهای بهینهسازی جایگزین بحث خواهیم کرد.
درک مسئله: رندرهای مجدد غیر ضروری
مسئله اصلی از این واقعیت ناشی می شود که React's Context API، به طور پیش فرض، هر زمان که مقدار Context تغییر می کند، یک رندر مجدد از تمام اجزای مصرف کننده را تحریک می کند. سناریویی را در نظر بگیرید که Context شما یک شی بزرگ حاوی داده های پروفایل کاربر، تنظیمات تم و پیکربندی برنامه را در خود جای داده است. اگر یک ویژگی را در پروفایل کاربر به روز کنید، تمام اجزای مصرف کننده Context دوباره رندر می شوند، حتی اگر فقط به تنظیمات تم متکی باشند.
این می تواند منجر به کاهش قابل توجه عملکرد شود، به ویژه هنگام برخورد با سلسله مراتب اجزای پیچیده و به روز رسانی های مکرر Context. رندرهای مجدد غیر ضروری، چرخه های ارزشمند CPU را هدر می دهند و می توانند منجر به رابط کاربری کند شوند.
الگوی انتخابگر: به روز رسانی های هدفمند
الگوی انتخابگر با اجازه دادن به اجزا برای اشتراک فقط در قسمت های خاصی از مقدار Context که به آن نیاز دارند، یک راه حل ارائه می دهد. به جای مصرف کل Context، اجزا از توابع انتخابگر برای استخراج داده های مربوطه استفاده می کنند. این امر دامنه رندرهای مجدد را کاهش می دهد و اطمینان می دهد که فقط اجزایی که واقعاً به داده های تغییر یافته وابسته هستند به روز می شوند.
نحوه کارکرد:
- Context Provider: Context Provider وضعیت برنامه را در خود جای می دهد.
- توابع انتخابگر: اینها توابع خالص هستند که مقدار Context را به عنوان ورودی می گیرند و یک مقدار مشتق شده را برمی گردانند. آنها به عنوان فیلتر عمل می کنند و قطعات خاصی از داده ها را از Context استخراج می کنند.
- اجزای مصرف کننده: اجزا از یک هوک سفارشی (اغلب به نام `useContextSelector`) برای اشتراک در خروجی یک تابع انتخابگر استفاده می کنند. این هوک مسئول تشخیص تغییرات در داده های انتخاب شده و تحریک رندر مجدد فقط در صورت لزوم است.
پیاده سازی الگوی انتخابگر
در اینجا یک مثال اساسی برای نشان دادن پیاده سازی الگوی انتخابگر آورده شده است:
1. ایجاد Context
ابتدا، Context خود را تعریف می کنیم. بیایید یک context برای مدیریت پروفایل کاربر و تنظیمات تم تصور کنیم.
import React, { createContext, useState, useContext } from 'react';
const AppContext = createContext({});
const AppProvider = ({ children }) => {
const [user, setUser] = useState({
name: 'John Doe',
email: 'john.doe@example.com',
location: 'New York'
});
const [theme, setTheme] = useState({
primaryColor: '#007bff',
secondaryColor: '#6c757d'
});
const updateUserName = (name) => {
setUser(prevUser => ({ ...prevUser, name }));
};
const updateThemeColor = (primaryColor) => {
setTheme(prevTheme => ({ ...prevTheme, primaryColor }));
};
const value = {
user,
theme,
updateUserName,
updateThemeColor
};
return (
{children}
);
};
export { AppContext, AppProvider };
2. ایجاد توابع انتخابگر
در مرحله بعد، توابع انتخابگر را برای استخراج داده های مورد نظر از Context تعریف می کنیم. مثلا:
const selectUserName = (context) => context.user.name;
const selectPrimaryColor = (context) => context.theme.primaryColor;
3. ایجاد یک هوک سفارشی (`useContextSelector`)
این هسته اصلی الگوی انتخابگر است. هوک `useContextSelector` یک تابع انتخابگر را به عنوان ورودی می گیرد و مقدار انتخاب شده را برمی گرداند. همچنین مدیریت اشتراک در Context را بر عهده دارد و رندر مجدد را فقط زمانی که مقدار انتخاب شده تغییر می کند تحریک می کند.
import { useContext, useState, useEffect, useRef } from 'react';
const useContextSelector = (context, selector) => {
const [selected, setSelected] = useState(() => selector(useContext(context)));
const latestSelector = useRef(selector);
const contextValue = useContext(context);
useEffect(() => {
latestSelector.current = selector;
});
useEffect(() => {
const nextSelected = latestSelector.current(contextValue);
if (!Object.is(selected, nextSelected)) {
setSelected(nextSelected);
}
}, [contextValue]);
return selected;
};
export default useContextSelector;
توضیحات:
- `useState`: `selected` را با مقدار اولیه برگشتی توسط انتخابگر مقداردهی اولیه کنید.
- `useRef`: آخرین تابع `selector` را ذخیره کنید، و اطمینان حاصل کنید که از جدیدترین انتخابگر استفاده می شود، حتی اگر جزء دوباره رندر شود.
- `useContext`: مقدار فعلی context را بدست آورید.
- `useEffect`: این افکت هر زمان که `contextValue` تغییر می کند اجرا می شود. در داخل، مقدار انتخاب شده را با استفاده از `latestSelector` دوباره محاسبه می کند. اگر مقدار انتخاب شده جدید با مقدار `selected` فعلی متفاوت باشد (با استفاده از `Object.is` برای مقایسه عمیق)، وضعیت `selected` به روز می شود و یک رندر مجدد را تحریک می کند.
4. استفاده از Context در اجزا
اکنون، اجزا می توانند از هوک `useContextSelector` برای اشتراک در بخش های خاصی از Context استفاده کنند:
import React from 'react';
import { AppContext, AppProvider } from './AppContext';
import useContextSelector from './useContextSelector';
const UserName = () => {
const userName = useContextSelector(AppContext, selectUserName);
return User Name: {userName}
;
};
const ThemeColorDisplay = () => {
const primaryColor = useContextSelector(AppContext, selectPrimaryColor);
return Theme Color: {primaryColor}
;
};
const App = () => {
return (
);
};
export default App;
در این مثال، `UserName` فقط زمانی دوباره رندر می شود که نام کاربر تغییر کند، و `ThemeColorDisplay` فقط زمانی دوباره رندر می شود که رنگ اصلی تغییر کند. تغییر ایمیل یا موقعیت مکانی کاربر باعث نمی شود که `ThemeColorDisplay` دوباره رندر شود و بالعکس.
مزایای الگوی انتخابگر
- کاهش رندرهای مجدد: مزیت اصلی، کاهش قابل توجه در رندرهای مجدد غیر ضروری است که منجر به بهبود عملکرد می شود.
- بهبود عملکرد: با به حداقل رساندن رندرهای مجدد، برنامه پاسخگوتر و کارآمدتر می شود.
- وضوح کد: توابع انتخابگر وضوح کد و قابلیت نگهداری را با تعریف صریح وابستگی های داده ای اجزا ترویج می کنند.
- قابلیت تست: توابع انتخابگر توابع خالص هستند و تست و استدلال در مورد آنها را آسان می کند.
ملاحظات و بهینه سازی ها
1. مِموسازی
مِموسازی می تواند عملکرد توابع انتخابگر را بیشتر افزایش دهد. اگر مقدار Context ورودی تغییر نکرده باشد، تابع انتخابگر می تواند یک نتیجه کش شده را برگرداند و از محاسبات غیر ضروری جلوگیری کند. این امر به ویژه برای توابع انتخابگر پیچیده که محاسبات پرهزینه انجام می دهند مفید است.
می توانید از هوک `useMemo` در پیاده سازی `useContextSelector` خود برای مِموسازی مقدار انتخاب شده استفاده کنید. این یک لایه دیگر از بهینه سازی را اضافه می کند و از رندرهای مجدد غیر ضروری حتی زمانی که مقدار context تغییر می کند، جلوگیری می کند، اما مقدار انتخاب شده یکسان باقی می ماند. در اینجا یک `useContextSelector` به روز شده با مِموسازی آورده شده است:
import { useContext, useState, useEffect, useRef, useMemo } from 'react';
const useContextSelector = (context, selector) => {
const latestSelector = useRef(selector);
const contextValue = useContext(context);
useEffect(() => {
latestSelector.current = selector;
}, [selector]);
const selected = useMemo(() => latestSelector.current(contextValue), [contextValue]);
return selected;
};
export default useContextSelector;
2. تغییر ناپذیری شی
اطمینان از تغییر ناپذیری مقدار Context برای عملکرد صحیح الگوی انتخابگر بسیار مهم است. اگر مقدار Context مستقیماً تغییر کند، توابع انتخابگر ممکن است تغییرات را تشخیص ندهند و منجر به رندر نادرست شوند. هنگام به روز رسانی مقدار Context، همیشه اشیاء یا آرایه های جدید ایجاد کنید.
3. مقایسه های عمیق
هوک `useContextSelector` از `Object.is` برای مقایسه مقادیر انتخاب شده استفاده می کند. این یک مقایسه سطحی انجام می دهد. برای اشیاء پیچیده، ممکن است لازم باشد از یک تابع مقایسه عمیق برای تشخیص دقیق تغییرات استفاده کنید. با این حال، مقایسه های عمیق می توانند از نظر محاسباتی پرهزینه باشند، بنابراین از آنها با احتیاط استفاده کنید.
4. جایگزین هایی برای `Object.is`
وقتی `Object.is` کافی نیست (به عنوان مثال، اشیاء عمیقاً تودرتو در context خود دارید)، جایگزین ها را در نظر بگیرید. کتابخانه هایی مانند `lodash`، `_.isEqual` را برای مقایسه های عمیق ارائه می دهند، اما از تأثیر عملکرد آگاه باشید. در برخی موارد، تکنیکهای اشتراکگذاری ساختاری با استفاده از ساختارهای داده تغییرناپذیر (مانند Immer) میتوانند مفید باشند زیرا به شما این امکان را میدهند که یک شیء تودرتو را بدون تغییر اصلی تغییر دهید، و اغلب میتوان آنها را با `Object.is` مقایسه کرد.
5. `useCallback` برای انتخابگرها
خود تابع `selector` می تواند منبع رندرهای مجدد غیر ضروری باشد اگر به درستی مِموسازی نشده باشد. تابع `selector` را به `useCallback` منتقل کنید تا مطمئن شوید که فقط زمانی دوباره ایجاد می شود که وابستگی های آن تغییر کنند. این از به روز رسانی های غیر ضروری به هوک سفارشی جلوگیری می کند.
const UserName = () => {
const userName = useContextSelector(AppContext, useCallback(selectUserName, []));
return User Name: {userName}
;
};
6. استفاده از کتابخانه هایی مانند `use-context-selector`
کتابخانه هایی مانند `use-context-selector` یک هوک `useContextSelector` از پیش ساخته شده ارائه می دهند که برای عملکرد بهینه شده است و شامل ویژگی هایی مانند مقایسه سطحی است. استفاده از چنین کتابخانه هایی می تواند کد شما را ساده کرده و خطر معرفی خطاها را کاهش دهد.
import { useContextSelector } from 'use-context-selector';
import { AppContext } from './AppContext';
const UserName = () => {
const userName = useContextSelector(AppContext, selectUserName);
return User Name: {userName}
;
};
مثال ها و بهترین شیوه های جهانی
الگوی انتخابگر در کاربردهای مختلف در برنامه های کاربردی جهانی قابل استفاده است:
- بومی سازی: یک پلتفرم تجارت الکترونیک را تصور کنید که از چندین زبان پشتیبانی می کند. Context می تواند زبان و ترجمه های فعلی را در خود جای دهد. اجزای نمایش متن می توانند از انتخابگرها برای استخراج ترجمه مربوطه برای زبان فعلی استفاده کنند.
- مدیریت تم: یک برنامه رسانه اجتماعی می تواند به کاربران اجازه دهد تا تم را سفارشی کنند. Context می تواند تنظیمات تم را ذخیره کند و اجزای نمایش عناصر UI می توانند از انتخابگرها برای استخراج ویژگی های تم مربوطه (به عنوان مثال، رنگ ها، فونت ها) استفاده کنند.
- احراز هویت: یک برنامه کاربردی سازمانی جهانی می تواند از Context برای مدیریت وضعیت احراز هویت کاربر و مجوزها استفاده کند. اجزا می توانند از انتخابگرها برای تعیین اینکه آیا کاربر فعلی به ویژگی های خاص دسترسی دارد یا خیر، استفاده کنند.
- وضعیت واکشی داده: بسیاری از برنامه ها حالت های بارگیری را نمایش می دهند. یک context می تواند وضعیت تماس های API را مدیریت کند و اجزا می توانند به طور انتخابی در وضعیت بارگیری نقاط پایانی خاص مشترک شوند. به عنوان مثال، یک جزء نمایش دهنده پروفایل کاربر ممکن است فقط در وضعیت بارگیری نقطه پایانی `GET /user/:id` مشترک شود.
تکنیک های بهینه سازی جایگزین
در حالی که الگوی انتخابگر یک تکنیک بهینه سازی قدرتمند است، تنها ابزار موجود نیست. این جایگزین ها را در نظر بگیرید:
- `React.memo`: اجزای تابعی را با `React.memo` بپیچید تا از رندرهای مجدد زمانی که prop ها تغییر نکرده اند جلوگیری کنید. این برای بهینه سازی اجزایی که مستقیماً prop ها را دریافت می کنند مفید است.
- `PureComponent`: از `PureComponent` برای اجزای کلاس استفاده کنید تا قبل از رندر مجدد، یک مقایسه سطحی از prop ها و وضعیت انجام دهید.
- تقسیم کد: برنامه را به قطعات کوچکتر تقسیم کنید که می توانند در صورت تقاضا بارگیری شوند. این زمان بارگذاری اولیه را کاهش می دهد و عملکرد کلی را بهبود می بخشد.
- مجازی سازی: برای نمایش لیست های بزرگ داده، از تکنیک های مجازی سازی برای رندر فقط موارد قابل مشاهده استفاده کنید. این امر هنگام برخورد با مجموعه داده های بزرگ به طور قابل توجهی عملکرد را بهبود می بخشد.
نتیجه گیری
الگوی انتخابگر یک تکنیک ارزشمند برای بهینه سازی عملکرد React Context با به حداقل رساندن رندرهای مجدد غیر ضروری است. با اجازه دادن به اجزا برای اشتراک فقط در بخش های خاصی از مقدار Context که به آن نیاز دارند، پاسخگویی و کارایی برنامه را بهبود می بخشد. با ترکیب آن با سایر تکنیکهای بهینهسازی مانند مِموسازی و تقسیم کد، میتوانید برنامههای React با کارایی بالا بسازید که یک تجربه کاربری روان را ارائه میدهند. به یاد داشته باشید که استراتژی بهینه سازی مناسب را بر اساس نیازهای خاص برنامه خود انتخاب کنید و با دقت ملاحظات مربوطه را در نظر بگیرید.
این مقاله یک راهنمای جامع برای الگوی انتخابگر، از جمله پیاده سازی، مزایا و ملاحظات آن ارائه داد. با پیروی از بهترین شیوه های ذکر شده در این مقاله، می توانید به طور موثر استفاده از React Context خود را بهینه کرده و برنامه های کاربردی با عملکرد بالا را برای مخاطبان جهانی بسازید.