مقدمه

React به صورت پیش‌فرض، با هر تغییر در state یا props یک کامپوننت را دوباره رندر می‌کند. این یعنی تمام کدهای داخل تابع کامپوننت، در هر بار رندر مجدد، از نو اجرا می‌شوند. این رفتار معمولاً مشکلی ایجاد نمی‌کند، اما اگر کامپوننت شما یک محاسبه سنگین و زمان‌بر را انجام دهد، اجرای مکرر آن می‌تواند باعث کندی و لگ در رابط کاربری شود.

هوک useMemo یک ابزار بهینه‌سازی است که به ما اجازه می‌دهد تا نتیجه یک محاسبه را «به خاطر بسپاریم» (memoize کنیم). useMemo نتیجه محاسبه را در حافظه کش کرده و تنها زمانی آن را دوباره محاسبه می‌کند که یکی از وابستگی‌های آن تغییر کرده باشد. در رندرهای دیگر، React به جای اجرای مجدد محاسبه، از نتیجه کش شده استفاده خواهد کرد.

useMemo در عمل

هوک useMemo یک تابع (که محاسبه را انجام می‌دهد) و یک آرایه وابستگی‌ها را به عنوان آرگومان می‌گیرد.

یک مثال بدون بهینه‌سازی

بیایید یک کامپوننت بسازیم که یک تابع سنگین را فراخوانی می‌کند.

Copy Icon JAVASCRIPT (React)
function TodoList({ todos }) {
  const [theme, setTheme] = useState('light');
  
  // This expensive function runs on EVERY render
  const visibleTodos = filterTodos(todos, tab);

  // ... return JSX
}

در این مثال، فرض کنید تابع filterTodos یک عملیات فیلتر کردن بسیار سنگین را روی یک لیست بزرگ از todos انجام می‌دهد. مشکل اینجاست که حتی اگر کاربر تم (theme) را تغییر دهد (که هیچ ربطی به لیست todos ندارد)، کامپوننت دوباره رندر شده و تابع سنگین filterTodos به صورت غیرضروری دوباره اجرا می‌شود.

بهینه‌سازی با useMemo

حالا بیایید این محاسبه را با useMemo بهینه کنیم.

Copy Icon JAVASCRIPT (React)
import { useMemo } from 'react';

function TodoList({ todos, tab }) {
  const [theme, setTheme] = useState('light');

  // This expensive function now only runs when `todos` or `tab` change.
  const visibleTodos = useMemo(
    () => filterTodos(todos, tab),
    [todos, tab]
  );

  // ... return JSX
}

در این نسخه بهینه‌سازی شده، ما تابع filterTodos را در داخل یک useMemo قرار داده‌ایم. آرایه وابستگی‌های [todos, tab] به React می‌گوید که این محاسبه تنها به todos و tab وابسته است.

اکنون، اگر کاربر تم را تغییر دهد، کامپوننت دوباره رندر می‌شود، اما React می‌بیند که مقادیر todos و tab تغییر نکرده‌اند. بنابراین، به جای اجرای مجدد تابع سنگین filterTodos نتیجه قبلی آن را که در حافظه کش شده است، مستقیماً برمی‌گرداند.

چه زمانی از useMemo استفاده کنیم؟

مهم است به خاطر داشته باشید که useMemo خود نیز کمی هزینه سربار دارد. شما نباید هر تابعی را در useMemo قرار دهید. useMemo را تنها برای موارد زیر به کار ببرید:

  • محاسبات واقعاً سنگین: تنها محاسباتی را memoize کنید که اجرای آنها به طور قابل توجهی زمان‌بر است.
  • جلوگیری از رندرهای مجدد غیرضروری در کامپوننت‌های فرزند: گاهی اوقات، شما یک شیء یا آرایه را در داخل یک کامپوننت می‌سازید و آن را به عنوان prop به یک کامپوننت فرزند پاس می‌دهید. اگر این کار را با useMemo بهینه نکنید، در هر بار رندر والد، یک شیء جدید ساخته شده و باعث رندر مجدد غیرضروری فرزند می‌شود.

در این درس، با هوک useMemo به عنوان یک ابزار بهینه‌سازی قدرتمند برای جلوگیری از محاسبات تکراری آشنا شدیم. دیدیم که چگونه می‌توان با به خاطر سپردن نتایج محاسبات سنگین، عملکرد اپلیکیشن‌های React خود را بهبود بخشید. در درس بعدی، به سراغ یکی از قوی‌ترین الگوها در React، یعنی «طراحی هوک سفارشی (Custom Hook)» خواهیم رفت و یاد می‌گیریم که چگونه منطق stateدار را بین کامپوننت‌های مختلف به اشتراک بگذاریم.