هوک useMemo (useMemo)
هوک useMemo در ری اکت کمک می کند یک مقدار سنگین را حساب کنی و همان را نگه داری تا هر بار بی خودی دوباره محاسبه نشود. مثل وقتی که جواب یک سؤال سخت ریاضی را گوشه دفترت می نویسی تا هر بار دوباره حلش نکنی.
useMemo در ری اکت دقیقاً چه کار می کند؟
هوک (Hook) یعنی تابع کمکی ری اکت که به کامپوننت قدرت اضافه می کند. useMemo در ری اکت یک مقدار «مموایز (Memoize)» برمی گرداند؛ یعنی مقدار را کش (Cache) می کند تا لازم نباشد دوباره حساب شود.
وقتی وابستگی ها یا همان dependencies عوض شوند، useMemo دوباره مقدار را محاسبه می کند. اگر وابستگی عوض نشود، همان مقدار قبلی را سریع برمی گرداند و این یعنی عملکرد بهتر مخصوصاً برای محاسبات سنگین.
در منبع گفته شده useMemo و useCallback شبیه اند. useMemo مقدار مموایز شده برمی گرداند، اما useCallback تابع مموایز شده برمی گرداند. برای useCallback می توانی بخش هوک useCallback را هم بعداً ببینی.
بدون استفاده از useMemo در ری اکت
حالا منبع یک مثال می زند که در آن یک تابع بسیار سنگین داریم. این تابع expensiveCalculation نام دارد و داخلش یک حلقه خیلی بزرگ وجود دارد. این تابع در هر رندر اجرا می شود و همین باعث کندی می شود.
وقتی count را زیاد می کنی یا یک todo جدید اضافه می کنی، این تابع دوباره اجرا می شود. منبع توضیح می دهد که حتی وقتی فقط todo اضافه می کنی و count عوض نشده، باز هم محاسبه سنگین انجام می شود.
مثال: اجرای محاسبه سنگین در هر رندر
import { useState } from "react";
import { createRoot } from "react-dom/client";
const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const calculation = expensiveCalculation(count);
const increment = () => {
setCount((c) => {
return c + 1;
});
};
const addTodo = () => {
setTodos((t) => {
return [...t, "New Todo"];
});
};
return (
<div>
<div>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return (
<p key={index}>
{todo}
</p>
);
})}
<button onClick={addTodo}>
Add Todo
</button>
</div>
<hr />
<div>
Count: {count}
<button onClick={increment}>
+
</button>
<h2>Expensive Calculation</h2>
{calculation}
<p>
Note that this example executes the expensive function also when you click on the Add Todo button.
</p>
</div>
</div>
);
};
const expensiveCalculation = (num) => {
console.log("Calculating...");
let i = 0;
while (i < 1000000000) {
num = num + 1;
i = i + 1;
}
return num;
};
createRoot(document.getElementById("root")).render(
<App />
);
در توضیح منبع آمده که هنگام کار با این مثال، وقتی count را تغییر می دهی یا todo اضافه می کنی، تاخیر را حس می کنی. چون expensiveCalculation در هر دو حالت دوباره اجرا می شود.
نکته: این وضعیت شبیه این است که هر بار معلم ازت نمره میانگین بپرسد و تو دوباره از اول تمام نمره ها را جمع بزنی، درحالی که می توانی آخرین جواب را ذخیره کنی.
بهینه سازی با useMemo در ری اکت
برای حل مشکل، منبع پیشنهاد می دهد که از useMemo در ری اکت استفاده کنیم. ایده این است که نتیجه expensiveCalculation را مموایز کنیم تا فقط وقتی count عوض شد دوباره حساب شود، نه وقتی todo ها تغییر کردند.
در نسخه بهینه، فراخوانی expensiveCalculation داخل useMemo پیچیده می شود. useMemo یک تابع و یک آرایه وابستگی می گیرد. اینجا وابستگی count است، پس فقط وقتی count تغییر کند، محاسبه سنگین دوباره انجام می شود.
مثال: استفاده از useMemo برای محاسبه سنگین
import { useState, useMemo } from "react";
import { createRoot } from "react-dom/client";
const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const calculation = useMemo(() => {
return expensiveCalculation(count);
}, [count]);
const increment = () => {
setCount((c) => {
return c + 1;
});
};
const addTodo = () => {
setTodos((t) => {
return [...t, "New Todo"];
});
};
return (
<div>
<div>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return (
<p key={index}>
{todo}
</p>
);
})}
<button onClick={addTodo}>
Add Todo
</button>
</div>
<hr />
<div>
Count: {count}
<button onClick={increment}>
+
</button>
<h2>Expensive Calculation</h2>
{calculation}
</div>
</div>
);
};
const expensiveCalculation = (num) => {
console.log("Calculating...");
let i = 0;
while (i < 1000000000) {
num = num + 1;
i = i + 1;
}
return num;
};
createRoot(document.getElementById("root")).render(
<App />
);
طبق توضیح منبع، در این نسخه وقتی فقط todo اضافه می کنی، expensiveCalculation دیگر اجرا نمی شود. چون count تغییری نکرده است. بنابراین رابط کاربری سریع تر و نرم تر حس می شود.
نکته: دقت کن که useMemo در ری اکت فقط زمانی مفید است که محاسبه واقعاً سنگین باشد. استفاده بی دلیل از آن، کد را شلوغ می کند.
اگر خواستی این صفحه را دوباره پیدا کنی، آن را به عنوان مرجع useMemo در ری اکت نگه دار. همچنین برای یادآوری تفاوت، صفحه هوک useCallback را هم کنار آن قرار بده.
جمع بندی سریع
- useMemo در ری اکت یک مقدار مموایز شده برمی گرداند.
- این هوک فقط وقتی وابستگی ها عوض شوند دوباره محاسبه می کند.
- برای محاسبات سنگین و تکراری، عملکرد را خیلی بهتر می کند.
- فرقش با useCallback این است که مقدار می دهد، نه تابع.
- برای منطق ساده، لازم نیست از useMemo در ری اکت استفاده کنی.