বিষয়: useMemo
দিয়ে Performance Optimization
কেন useMemo
দরকার?
React-এ যখন কোনো কম্পোনেন্ট রি-রেন্ডার হয়, তখন তার ভিতরে থাকা সব কিছু—including ফাংশন ও ক্যালকুলেশন—আবার চালানো হয়। যদি কোনো ফাংশন বারবার চালানো ব্যয়বহুল (expensive) হয়, যেমন:
- বড়
while
লুপ - Complex math calculation
- Time consuming API call
তাহলে প্রতিবার রি-রেন্ডারে পারফরম্যান্স খারাপ হতে পারে।
🧪 সমাধান: useMemo
useMemo
হুক ব্যবহার করে আপনি একটি ফাংশনের রিটার্ন ভ্যালু মেমোরাইজ করে রাখতে পারেন।
মানে: যদি নির্দিষ্ট কোন স্টেট না বদলায়, তাহলে React আগের ভ্যালুটাই ব্যবহার করবে, নতুন করে ক্যালকুলেশন করবে না।
✅ উদাহরণ ১: সাধারণ ফাংশন
function isEven(num) {
return num % 2 === 0;
}
এখন এই ফাংশন যদি অনেক সময় খরচ করে, যেমন:
function isEven(num) {
let i = 0;
while (i < 1000000000) i++;
return num % 2 === 0;
}
তাহলে এটা প্রতিবার কম্পোনেন্ট রি-রেন্ডার হলে পুরো লুপ চলবে, অ্যাপ হ্যাং করে যেতে পারে।
✅ সমাধান: useMemo দিয়ে মেমোরাইজ করা
const isEven = useMemo(() => {
let i = 0;
while (i < 1000000000) i++;
return countOne % 2 === 0;
}, [countOne]);
🔁 এখানে [countOne]
হল dependency array।
এই ভ্যালু না বদলালে useMemo
আগের ক্যালকুলেট করা ভ্যালু রিটার্ন করে।
<span>{isEven ? "Even" : "Odd"}</span>
মজার কথা: useMemo নিজেই “expensive” হতে পারে
useMemo
ব্যবহার করলেও React-কে একটা কম্প্যারিজন করতে হয়:
“ডিপেন্ডেন্সি চেঞ্জ হয়েছে কি না?”
এই কাজটাও সামান্য ব্যয়বহুল। তাই ছোট-খাটো কাজের জন্য useMemo
না ব্যবহার করাই ভালো।
চেক করুন ফাংশন ক্যালকুলেশন হতে কত সময় লাগে
console.time("filter array");
const visibleTodos = getFilteredTodos(todos, filter);
console.timeEnd("filter array");
এবং useMemo ব্যবহার করলে কত সময় লাগবে ক্যালকুলেশনে
console.time("filter array");
const visibleTodos = useMemo(() => {
return getFilteredTodos(todos, filter); // Skipped if todos and filter haven't changed
}, [todos, filter]);
console.timeEnd("filter array");
🔑 কখন ব্যবহার করবেন?
কাজটি | useMemo দরকার কি? |
---|---|
Simple text format বা সংখ্যা গণনা | ❌ দরকার নেই |
বড় লুপ, ভারি ক্যালকুলেশন | ✅ useMemo ব্যবহার করুন |
Child Component রি-রেন্ডার ঠেকাতে | ✅ ব্যবহার করা যায়, তবে React.memo -র সঙ্গে মিলিয়ে চিন্তা করুন |
📌 মনে রাখার নিয়ম
“Performance Optimization তখনই করবেন, যখন সত্যিই পারফরম্যান্স সমস্যা হচ্ছে।” আগেই না বুঝে
useMemo
,useCallback
ব্যবহার করলে কোড জটিল হবে, উপকার নাও হতে পারে।
🧩 চূড়ান্ত কোড উদাহরণ
import { useState, useMemo } from "react";
export default function App() {
const [countOne, setCountOne] = useState(0);
const [countTwo, setCountTwo] = useState(0);
const isEven = useMemo(() => {
let i = 0;
while (i < 1000000000) i++;
return countOne % 2 === 0;
}, [countOne]);
return (
<div>
<h1>{countOne}</h1>
<button onClick={() => setCountOne(countOne + 1)}>Increment by 1</button>
<h1>{countTwo}</h1>
<button onClick={() => setCountTwo(countTwo + 5)}>Increment by 5</button>
<p>{isEven ? "Even" : "Odd"}</p>
</div>
);
}
🔹 useMemo
দিয়ে React এ পারফরম্যান্স অপ্টিমাইজেশন
কখনো এমন হতে পারে যে আপনার অ্যাপে কিছু ফাংশন এমন আছে, যা বারবার রান করলে অ্যাপ স্লো হয়ে যায়। এই সময় আপনি useMemo
হুক ব্যবহার করতে পারেন, যাতে React আগের ক্যালকুলেট করা ভ্যালু মনে রেখে দেয়। useMemo
শুধু তখনই নতুন করে ক্যালকুলেট করে, যখন নির্দিষ্ট কোন ভ্যালু (dependency) চেঞ্জ হয়।
এই অধ্যায়ে আমরা দেখেছি কিভাবে useMemo
এর সাহায্যে একটি “isEven” টাইপের কস্টলি ফাংশনকে মেমোরাইজ করে রাখা যায়, যাতে রেন্ডার টাইমে অপ্রয়োজনীয় লোড না পড়ে।
useCallback
ও useMemo
দেখতে অনেকটা একই রকম হলেও, এদের কাজ ও ব্যবহারের সময় আলাদা।
🧠 মূল পার্থক্য: useCallback
vs useMemo
বিষয় | useCallback | useMemo |
---|---|---|
🔧 কী রিটার্ন করে | একটা মেমোরাইজড ফাংশন | একটা মেমোরাইজড ভ্যালু |
🎯 উদ্দেশ্য | কোনো ফাংশনকে রেন্ডারে বারবার তৈরি হওয়া থেকে বাঁচানো | কোনো ক্যালকুলেশন বারবার চালানো থেকে বাঁচানো |
🧪 কবে দরকার | যখন ফাংশনটা কোনো চাইল্ড কম্পোনেন্টকে প্রপ হিসেবে পাঠাতে হয় | যখন কোনো expensive calculation আছে |
📦 রিটার্ন | () => {...} (একটা ফাংশন) | কোনো ভ্যালু, যেমন number , string , boolean , object |
📘 সিনট্যাক্স পার্থক্য
✅ useCallback
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
🔁 এটি রিটার্ন করে একটি ফাংশন যা a
বা b
না বদলালে আগেরটাই থাকবে।
✅ useMemo
const memoizedValue = useMemo(() => {
return expensiveFunction(a, b);
}, [a, b]);
🔁 এটি রিটার্ন করে একটি ভ্যালু (যেমন number
, boolean
, object
) যা a
বা b
না বদলালে আগেরটাই ব্যবহার হয়।
📌 কবে কোনটা ব্যবহার করব?
🎯 যখন useCallback
ব্যবহার করবেন:
- যখন আপনি কোনো event handler ফাংশন নিচে
props
দিয়ে পাঠাচ্ছেন - অথবা যখন
React.memo()
করা child component আছে, যেটা প্রপ হিসেবে ফাংশন নিচ্ছে
✅ উদাহরণ:
const handleClick = useCallback(() => {
setCount((c) => c + 1);
}, []);
🎯 যখন useMemo
ব্যবহার করবেন:
- যখন আপনি কোনো ভ্যালু ক্যালকুলেশন করছেন যেটা ভারী বা সময়সাপেক্ষ
- এবং আপনি চান যেন dependency না বদলালে সেটা আবার ক্যালকুলেট না হয়
✅ উদাহরণ:
const isEven = useMemo(() => {
return count % 2 === 0;
}, [count]);
🔁 এক লাইনে পার্থক্য:
🧩
useCallback(fn, deps)
===useMemo(() => fn, deps)
মানে, useCallback
শুধু useMemo
-এর শর্টকাট যা ফাংশনকে মেমোরাইজ করে।
🧪 বাস্তব উদাহরণঃ
const expensiveCalculation = useMemo(() => {
let sum = 0;
for (let i = 0; i < 1000000; i++) sum += i;
return sum;
}, []);
const handleClick = useCallback(() => {
console.log("Button clicked");
}, []);
🎓 উপসংহার
যদি... | ব্যবহার করুন |
---|---|
মেমোরাইজ করতে চান কোনো ফাংশনকে | useCallback |
মেমোরাইজ করতে চান কোনো ভ্যালু বা ক্যালকুলেশনকে | useMemo |
চলুন, আমরা সেকশন ২.৫ এর দ্বিতীয় পার্ট—Caching expensive calculations—একদম সহজ, প্রাঞ্জল ভাষায় লিখে ফেলি যেন আপনার React Bangla বইয়ের জন্য সরাসরি ব্যবহার করা যায় এবং ভাইয়ের মতো বুঝিয়ে শেখা যায়।
✨ কস্টলি ক্যালকুলেশন মেমোরাইজ করা (useMemo দিয়ে)
🔶 ধরো, তুমি একটি টুডু অ্যাপ বানাচ্ছো
তোমার TodoList
নামে একটি কম্পোনেন্ট আছে, যেখানে ইউজারদের সব টাস্ক (todos
) আর একটা ফিল্টার অপশন (filter
) আসে props হিসেবে। এখন তুমি ইউজারকে শুধু ফিল্টার করা টাস্কগুলো দেখাতে চাও।
তো তুমি হয়তো এমন কিছু করতে চাও:
function TodoList({ todos, filter }) {
const [visibleTodos, setVisibleTodos] = useState([]);
useEffect(() => {
const filtered = getFilteredTodos(todos, filter);
setVisibleTodos(filtered);
}, [todos, filter]);
}
👎 এই কোডে দুইটা সমস্যা:
- তুমি অপ্রয়োজনীয় একটা স্টেট রাখছো (
visibleTodos
), যেটা মূলত অন্য props দিয়ে বানানো যায়। - তুমি useEffect ব্যবহার করছো যেটা এখানে দরকার নেই।
✅ তাহলে ঠিক কোড কী হতে পারে?
function TodoList({ todos, filter }) {
const visibleTodos = getFilteredTodos(todos, filter);
}
এইটা একদম ঠিক কাজ করে যদি getFilteredTodos()
ফাংশনটা খুব হালকা হয় (মানে দ্রুত চলে)।
❗কিন্তু...
যদি getFilteredTodos()
ফাংশনের ভিতরে অনেক বড় লজিক থাকে (যেমন: হাজার হাজার টুডু ফিল্টার করে), তাহলে ছোটখাট কোনো স্টেট আপডেট হলেই পুরো ফাংশন আবার চলবে। এতে পারফরম্যান্স খারাপ হতে পারে।
যেমন:
const [newTodo, setNewTodo] = useState("");
newTodo
পরিবর্তন হলেও পুরো ফিল্টার ফাংশন আবার চলে যাবে — অথচ তার সঙ্গে ফিল্টারের কোনো সম্পর্কই নেই!
✅ সমাধান: useMemo দিয়ে মেমোরাইজ করা
const visibleTodos = useMemo(() => {
return getFilteredTodos(todos, filter);
}, [todos, filter]);
এখানে React শুধু তখনই getFilteredTodos()
চালাবে, যখন todos
বা filter
বদলাবে।
তুমি যতবারই newTodo
টাইপ করো, এটা আর চলবে না — আগের রেজাল্টই ব্যবহার করবে।
🔍 এক লাইনে লেখা যায়:
const visibleTodos = useMemo(
() => getFilteredTodos(todos, filter),
[todos, filter]
);
এটা একদম ঠিক কাজ করবে। সোজা, ক্লিন এবং পারফরম্যান্স ফ্রেন্ডলি।
🔬 কিভাবে বুঝবেন যে ক্যালকুলেশনটা "expensive"?
সোজা উপায়:
console.time("filter array");
const visibleTodos = getFilteredTodos(todos, filter);
console.timeEnd("filter array");
এই কোডটা কনসোলে টাইম মাপে। যদি filter array: 2ms
বা তার বেশি টাইম লাগে, তাহলে ভাবা যায় এটা অপ্টিমাইজ করা দরকার।
এখন তুমি useMemo দিয়ে আবার চেক করতে পারো:
console.time("filter array");
const visibleTodos = useMemo(() => {
return getFilteredTodos(todos, filter);
}, [todos, filter]);
console.timeEnd("filter array");
⚠️ কিছু গুরুত্বপূর্ণ কথা:
useMemo
প্রথম রেন্ডারে কখনো পারফরম্যান্স বাড়ায় না।- এটি শুধু পরবর্তী রেন্ডারে অপ্রয়োজনীয় ক্যালকুলেশন আটকায়।
useMemo
দিয়ে শুধু pure calculation মেমোরাইজ করা উচিত (যে ফাংশনে DOM টাচ হয় না)।- ডেভেলপমেন্ট মোডে টাইমিং ঠিক পাওয়া যায় না — প্রোডাকশন বিল্ডে টেস্ট করুন।
🎯 আপনার ভাইয়ের মতো কিছু পরামর্শ:
যদি তুমি... | তাহলে |
---|---|
ছোট অ্যারে নিয়ে কাজ করছো | useMemo লাগবে না |
টাইপ করলে অ্যাপ স্লো হয়ে যাচ্ছে | useMemo দিয়ে চেক করো |
বড় object বা লম্বা লুপ চলে বারবার | তখন useMemo দিয়ে মেমোরাইজ করো |
কাজটা UI-তে সরাসরি প্রভাব ফেলে না | তাহলে সেটাকে useMemo করাই ভালো |
✅ চূড়ান্ত কোড উদাহরণ:
import { useState, useMemo } from "react";
function getFilteredTodos(todos, filter) {
return todos.filter((todo) => {
if (filter === "all") return true;
if (filter === "active") return !todo.completed;
if (filter === "completed") return todo.completed;
});
}
function TodoList({ todos, filter }) {
const [newTodo, setNewTodo] = useState("");
const visibleTodos = useMemo(() => {
return getFilteredTodos(todos, filter);
}, [todos, filter]);
return (
<div>
<input
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="Add todo..."
/>
<ul>
{visibleTodos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</div>
);
}
✍️ আপনার বইয়ে এই অংশ এইভাবে দিতে পারেন:
🔹 React-এ ভারী ক্যালকুলেশন মেমোরাইজ করা – useMemo দিয়ে সহজ অপ্টিমাইজেশন
React এ কাজ করার সময় অনেক সময় এমন হয়, তুমি এমন একটা ফাংশন ব্যবহার করছো যেটা বারবার চালালে অ্যাপ ধীর হয়ে যায়। যেমন: বড় লিস্ট ফিল্টার করা। এইরকম ফাংশনের রেজাল্ট বারবার ক্যালকুলেট করা দরকার নেই — কারণ সেই রেজাল্ট আগেই পাওয়া গেছে।
এই জন্যই আছে useMemo
— এটা তোমাকে বলে: "ভাই, যদি dependency না বদলায়, তাহলে আগের রেজাল্টই আমি দিয়ে দিবো।"
এই সহজ অথচ শক্তিশালী হুক তোমার অ্যাপকে করে তুলতে পারে অনেক দ্রুত, বিশেষ করে বড় ডেটা বা ফিল্টারিং নিয়ে কাজ করলে।