تکنیکهای پیشرفته مموایزیشن در ریاکت را برای بهینهسازی عملکرد در اپلیکیشنهای جهانی کاوش کنید. یاد بگیرید چه زمانی و چگونه از React.memo، useCallback، useMemo و موارد دیگر برای ساخت رابطهای کاربری کارآمد استفاده کنید.
React Memo: بررسی عمیق تکنیکهای بهینهسازی برای اپلیکیشنهای جهانی
ریاکت یک کتابخانه قدرتمند جاوااسکریپت برای ساخت رابطهای کاربری است، اما با افزایش پیچیدگی اپلیکیشنها، بهینهسازی عملکرد اهمیت حیاتی پیدا میکند. یکی از ابزارهای ضروری در جعبهابزار بهینهسازی ریاکت React.memo
است. این پست وبلاگ یک راهنمای جامع برای درک و استفاده مؤثر از React.memo
و تکنیکهای مرتبط برای ساخت اپلیکیشنهای ریاکت با کارایی بالا برای مخاطبان جهانی ارائه میدهد.
React.memo چیست؟
React.memo
یک کامپوننت مرتبه بالا (HOC) است که یک کامپوننت تابعی را مموایز (memoize) میکند. به زبان سادهتر، از رندر مجدد یک کامپوننت در صورتی که props آن تغییر نکرده باشد، جلوگیری میکند. به طور پیشفرض، این کار را با مقایسه سطحی (shallow comparison) propsها انجام میدهد. این امر میتواند به طور قابل توجهی عملکرد را بهبود بخشد، بهویژه برای کامپوننتهایی که رندر آنها از نظر محاسباتی سنگین است یا حتی زمانی که propsهایشان ثابت میماند، به طور مکرر رندر میشوند.
کامپوننتی را تصور کنید که پروفایل یک کاربر را نمایش میدهد. اگر اطلاعات کاربر (مانند نام، آواتار) تغییر نکرده باشد، نیازی به رندر مجدد کامپوننت نیست. React.memo
به شما اجازه میدهد از این رندر مجدد غیرضروری صرفنظر کنید و در زمان پردازش ارزشمند صرفهجویی کنید.
چرا از React.memo استفاده کنیم؟
در اینجا مزایای کلیدی استفاده از React.memo
آمده است:
- بهبود عملکرد: از رندرهای مجدد غیرضروری جلوگیری کرده و منجر به رابطهای کاربری سریعتر و روانتر میشود.
- کاهش استفاده از CPU: رندرهای کمتر به معنای استفاده کمتر از CPU است که بهویژه برای دستگاههای موبایل و کاربرانی که در مناطق با پهنای باند محدود هستند، اهمیت دارد.
- تجربه کاربری بهتر: یک اپلیکیشن واکنشگرا تجربه کاربری بهتری را فراهم میکند، بهویژه برای کاربرانی که اتصال اینترنت کندتر یا دستگاههای قدیمیتری دارند.
استفاده پایه از React.memo
استفاده از React.memo
ساده است. کافی است کامپوننت تابعی خود را با آن بپوشانید:
import React from 'react';
const MyComponent = (props) => {
console.log('MyComponent rendered');
return (
{props.data}
);
};
export default React.memo(MyComponent);
در این مثال، MyComponent
فقط در صورتی رندر مجدد میشود که prop با نام data
تغییر کند. عبارت console.log
به شما کمک میکند تا تأیید کنید که کامپوننت چه زمانی واقعاً رندر مجدد میشود.
درک مقایسه سطحی (Shallow Comparison)
بهطور پیشفرض، React.memo
یک مقایسه سطحی (shallow comparison) از propsها انجام میدهد. این به این معنی است که بررسی میکند آیا ارجاع (references) به propsها تغییر کرده است یا نه، نه خود مقادیر. درک این موضوع هنگام کار با اشیاء و آرایهها مهم است.
مثال زیر را در نظر بگیرید:
import React, { useState } from 'react';
const MyComponent = (props) => {
console.log('MyComponent rendered');
return (
{props.data.name}
);
};
const MemoizedComponent = React.memo(MyComponent);
const App = () => {
const [user, setUser] = useState({ name: 'John', age: 30 });
const handleClick = () => {
setUser({ ...user }); // Creating a new object with the same values
};
return (
);
};
export default App;
در این حالت، حتی اگر مقادیر شیء user
(یعنی name
و age
) ثابت باقی بمانند، تابع handleClick
هر بار که فراخوانی میشود، یک ارجاع شیء جدید ایجاد میکند. بنابراین، React.memo
تشخیص میدهد که prop با نام data
تغییر کرده است (زیرا ارجاع شیء متفاوت است) و MyComponent
را رندر مجدد میکند.
تابع مقایسه سفارشی
برای حل مشکل مقایسه سطحی با اشیاء و آرایهها، React.memo
به شما اجازه میدهد تا یک تابع مقایسه سفارشی را به عنوان آرگومان دوم آن ارائه دهید. این تابع دو آرگومان میگیرد: prevProps
و nextProps
. اگر کامپوننت نباید رندر مجدد شود (یعنی propsها عملاً یکسان هستند) باید true
برگرداند و اگر باید رندر مجدد شود، false
برگرداند.
در اینجا نحوه استفاده از یک تابع مقایسه سفارشی در مثال قبلی آمده است:
import React, { useState, memo } from 'react';
const MyComponent = (props) => {
console.log('MyComponent rendered');
return (
{props.data.name}
);
};
const areEqual = (prevProps, nextProps) => {
return prevProps.data.name === nextProps.data.name && prevProps.data.age === nextProps.data.age;
};
const MemoizedComponent = memo(MyComponent, areEqual);
const App = () => {
const [user, setUser] = useState({ name: 'John', age: 30 });
const handleClick = () => {
setUser({ ...user });
};
return (
);
};
export default App;
در این مثال بهروز شده، تابع areEqual
ویژگیهای name
و age
اشیاء user
را مقایسه میکند. اکنون MemoizedComponent
فقط در صورتی رندر مجدد میشود که name
یا age
تغییر کند.
چه زمانی از React.memo استفاده کنیم؟
React.memo
در سناریوهای زیر بیشترین تأثیر را دارد:
- کامپوننتهایی که به طور مکرر propsهای یکسان دریافت میکنند: اگر propsهای یک کامپوننت به ندرت تغییر میکنند، استفاده از
React.memo
میتواند از رندرهای مجدد غیرضروری جلوگیری کند. - کامپوننتهایی که رندر آنها از نظر محاسباتی سنگین است: برای کامپوننتهایی که محاسبات پیچیده انجام میدهند یا مقادیر زیادی داده را رندر میکنند، صرفنظر کردن از رندرهای مجدد میتواند عملکرد را به طور قابل توجهی بهبود بخشد.
- کامپوننتهای تابعی خالص (Pure functional components): کامپوننتهایی که برای ورودی یکسان، خروجی یکسان تولید میکنند، کاندیداهای ایدهآلی برای
React.memo
هستند.
با این حال، مهم است توجه داشته باشید که React.memo
یک راهحل جادویی نیست. استفاده بیرویه از آن در واقع میتواند به عملکرد آسیب برساند زیرا خود مقایسه سطحی هزینهای دارد. بنابراین، بسیار مهم است که اپلیکیشن خود را پروفایل کنید و کامپوننتهایی را که بیشترین سود را از مموایزیشن میبرند، شناسایی کنید.
جایگزینهای React.memo
در حالی که React.memo
ابزار قدرتمندی است، تنها گزینه برای بهینهسازی عملکرد کامپوننتهای ریاکت نیست. در اینجا چند جایگزین و تکنیک مکمل آورده شده است:
۱. PureComponent
برای کامپوننتهای کلاسی، PureComponent
عملکردی مشابه React.memo
ارائه میدهد. این کامپوننت یک مقایسه سطحی از props و state انجام میدهد و تنها در صورت وجود تغییرات، رندر مجدد میکند.
import React from 'react';
class MyComponent extends React.PureComponent {
render() {
console.log('MyComponent rendered');
return (
{this.props.data}
);
}
}
export default MyComponent;
PureComponent
یک جایگزین مناسب برای پیادهسازی دستی shouldComponentUpdate
است که روش سنتی برای جلوگیری از رندرهای مجدد غیرضروری در کامپوننتهای کلاسی بود.
۲. shouldComponentUpdate
shouldComponentUpdate
یک متد چرخه حیات در کامپوننتهای کلاسی است که به شما امکان میدهد منطق سفارشی برای تعیین اینکه آیا یک کامپوننت باید رندر مجدد شود یا نه، تعریف کنید. این متد بیشترین انعطافپذیری را فراهم میکند، اما به تلاش دستی بیشتری نیز نیاز دارد.
import React from 'react';
class MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return nextProps.data !== this.props.data;
}
render() {
console.log('MyComponent rendered');
return (
{this.props.data}
);
}
}
export default MyComponent;
در حالی که shouldComponentUpdate
هنوز در دسترس است، PureComponent
و React.memo
به طور کلی به دلیل سادگی و سهولت استفاده ترجیح داده میشوند.
۳. useCallback
useCallback
یک هوک ریاکت است که یک تابع را مموایز میکند. این هوک یک نسخه مموایز شده از تابع را برمیگرداند که تنها در صورتی تغییر میکند که یکی از وابستگیهای آن تغییر کرده باشد. این امر به ویژه برای ارسال callbackها به عنوان prop به کامپوننتهای مموایز شده مفید است.
مثال زیر را در نظر بگیرید:
import React, { useState, useCallback, memo } from 'react';
const MyComponent = (props) => {
console.log('MyComponent rendered');
return (
);
};
const MemoizedComponent = memo(MyComponent);
const App = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
Count: {count}
);
};
export default App;
در این مثال، useCallback
تضمین میکند که تابع handleClick
تنها زمانی تغییر میکند که state با نام count
تغییر کند. بدون useCallback
، یک تابع جدید در هر رندر App
ایجاد میشد و باعث رندر مجدد غیرضروری MemoizedComponent
میشد.
۴. useMemo
useMemo
یک هوک ریاکت است که یک مقدار را مموایز میکند. این هوک یک مقدار مموایز شده را برمیگرداند که تنها در صورتی تغییر میکند که یکی از وابستگیهای آن تغییر کرده باشد. این برای جلوگیری از محاسبات سنگینی که نیازی به اجرای مجدد در هر رندر ندارند، مفید است.
import React, { useState, useMemo } from 'react';
const App = () => {
const [input, setInput] = useState('');
const expensiveCalculation = (str) => {
console.log('Calculating...');
let result = 0;
for (let i = 0; i < str.length * 1000000; i++) {
result++;
}
return result;
};
const memoizedResult = useMemo(() => expensiveCalculation(input), [input]);
return (
setInput(e.target.value)} />
Result: {memoizedResult}
);
};
export default App;
در این مثال، useMemo
تضمین میکند که تابع expensiveCalculation
تنها زمانی فراخوانی میشود که state با نام input
تغییر کند. این از اجرای مجدد محاسبه در هر رندر جلوگیری میکند که میتواند عملکرد را به طور قابل توجهی بهبود بخشد.
مثالهای عملی برای اپلیکیشنهای جهانی
بیایید چند مثال عملی از نحوه استفاده از React.memo
و تکنیکهای مرتبط در اپلیکیشنهای جهانی را بررسی کنیم:
۱. انتخابگر زبان
یک کامپوننت انتخابگر زبان اغلب لیستی از زبانهای موجود را رندر میکند. این لیست ممکن است نسبتاً ثابت باشد، به این معنی که به طور مکرر تغییر نمیکند. استفاده از React.memo
میتواند از رندر مجدد غیرضروری انتخابگر زبان هنگام بهروزرسانی سایر بخشهای اپلیکیشن جلوگیری کند.
import React, { memo } from 'react';
const LanguageItem = ({ language, onSelect }) => {
console.log(`LanguageItem ${language} rendered`);
return (
onSelect(language)}>{language}
);
};
const MemoizedLanguageItem = memo(LanguageItem);
const LanguageSelector = ({ languages, onSelect }) => {
return (
{languages.map((language) => (
))}
);
};
export default LanguageSelector;
در این مثال، MemoizedLanguageItem
فقط در صورتی رندر مجدد میشود که prop با نام language
یا onSelect
تغییر کند. این امر به ویژه اگر لیست زبان طولانی باشد یا اگر کنترلکننده onSelect
پیچیده باشد، میتواند مفید باشد.
۲. مبدل ارز
یک کامپوننت مبدل ارز ممکن است لیستی از ارزها و نرخهای تبادل آنها را نمایش دهد. نرخهای تبادل ممکن است به صورت دورهای بهروز شوند، اما لیست ارزها ممکن است نسبتاً پایدار باقی بماند. استفاده از React.memo
میتواند از رندر مجدد غیرضروری لیست ارزها هنگام بهروزرسانی نرخهای تبادل جلوگیری کند.
import React, { memo } from 'react';
const CurrencyItem = ({ currency, rate, onSelect }) => {
console.log(`CurrencyItem ${currency} rendered`);
return (
onSelect(currency)}>{currency} - {rate}
);
};
const MemoizedCurrencyItem = memo(CurrencyItem);
const CurrencyConverter = ({ currencies, onSelect }) => {
return (
{Object.entries(currencies).map(([currency, rate]) => (
))}
);
};
export default CurrencyConverter;
در این مثال، MemoizedCurrencyItem
فقط در صورتی رندر مجدد میشود که prop با نام currency
، rate
یا onSelect
تغییر کند. این میتواند عملکرد را بهبود بخشد اگر لیست ارزها طولانی باشد یا اگر بهروزرسانیهای نرخ تبادل مکرر باشند.
۳. نمایش پروفایل کاربر
نمایش پروفایل کاربر شامل نشان دادن اطلاعات ثابتی مانند نام، تصویر پروفایل و احتمالاً بیوگرافی است. استفاده از `React.memo` تضمین میکند که کامپوننت فقط زمانی رندر مجدد میشود که دادههای کاربر واقعاً تغییر کنند، نه در هر بهروزرسانی کامپوننت والد.
import React, { memo } from 'react';
const UserProfile = ({ user }) => {
console.log('UserProfile rendered');
return (
{user.name}
{user.bio}
);
};
export default memo(UserProfile);
این امر به ویژه زمانی مفید است که `UserProfile` بخشی از یک داشبورد یا اپلیکیشن بزرگتر و به طور مکرر در حال بهروزرسانی باشد که در آن دادههای خود کاربر اغلب تغییر نمیکنند.
اشتباهات رایج و نحوه جلوگیری از آنها
در حالی که React.memo
یک ابزار بهینهسازی ارزشمند است، مهم است که از اشتباهات رایج آگاه باشید و از آنها اجتناب کنید:
- مموایزیشن بیش از حد: استفاده بیرویه از
React.memo
در واقع میتواند به عملکرد آسیب برساند زیرا خود مقایسه سطحی هزینهای دارد. فقط کامپوننتهایی را مموایز کنید که احتمالاً از آن سود میبرند. - آرایههای وابستگی نادرست: هنگام استفاده از
useCallback
وuseMemo
، اطمینان حاصل کنید که آرایههای وابستگی صحیح را ارائه میدهید. حذف وابستگیها یا گنجاندن وابستگیهای غیرضروری میتواند منجر به رفتار غیرمنتظره و مشکلات عملکردی شود. - تغییر دادن propsها: از تغییر مستقیم propsها خودداری کنید، زیرا این کار میتواند مقایسه سطحی
React.memo
را دور بزند. همیشه هنگام بهروزرسانی propsها اشیاء یا آرایههای جدیدی ایجاد کنید. - منطق مقایسه پیچیده: از منطق مقایسه پیچیده در توابع مقایسه سفارشی خودداری کنید، زیرا این میتواند مزایای عملکردی
React.memo
را خنثی کند. منطق مقایسه را تا حد امکان ساده و کارآمد نگه دارید.
پروفایل کردن اپلیکیشن شما
بهترین راه برای تعیین اینکه آیا React.memo
واقعاً عملکرد را بهبود میبخشد، پروفایل کردن اپلیکیشن شماست. ریاکت چندین ابزار برای پروفایل کردن فراهم میکند، از جمله React DevTools Profiler و React.Profiler
API.
React DevTools Profiler به شما امکان میدهد ردپاهای عملکرد اپلیکیشن خود را ضبط کرده و کامپوننتهایی را که به طور مکرر رندر مجدد میشوند، شناسایی کنید. React.Profiler
API به شما امکان میدهد زمان رندر کامپوننتهای خاص را به صورت برنامهنویسی اندازهگیری کنید.
با پروفایل کردن اپلیکیشن خود، میتوانید کامپوننتهایی را که بیشترین سود را از مموایزیشن میبرند شناسایی کرده و اطمینان حاصل کنید که React.memo
واقعاً عملکرد را بهبود میبخشد.
نتیجهگیری
React.memo
یک ابزار قدرتمند برای بهینهسازی عملکرد کامپوننتهای ریاکت است. با جلوگیری از رندرهای مجدد غیرضروری، میتواند سرعت و واکنشپذیری اپلیکیشنهای شما را بهبود بخشد و منجر به تجربه کاربری بهتری شود. با این حال، مهم است که از React.memo
با دقت استفاده کنید و اپلیکیشن خود را پروفایل کنید تا اطمینان حاصل کنید که واقعاً عملکرد را بهبود میبخشد.
با درک مفاهیم و تکنیکهای مورد بحث در این پست وبلاگ، میتوانید به طور مؤثر از React.memo
و تکنیکهای مرتبط برای ساخت اپلیکیشنهای ریاکت با کارایی بالا برای مخاطبان جهانی استفاده کنید و اطمینان حاصل کنید که اپلیکیشنهای شما برای کاربران در سراسر جهان سریع و واکنشگرا هستند.
به یاد داشته باشید که هنگام بهینهسازی اپلیکیشنهای ریاکت خود، عوامل جهانی مانند تأخیر شبکه و قابلیتهای دستگاه را در نظر بگیرید. با تمرکز بر عملکرد و دسترسیپذیری، میتوانید اپلیکیشنهایی ایجاد کنید که تجربه فوقالعادهای را برای همه کاربران، صرفنظر از مکان یا دستگاه آنها، فراهم میکنند.