Advance React শিখুন বাংলায় (পর্ব ৩) — useMemo দিয়ে পারফরম্যান্স অপ্টিমাইজেশন শিখুন

useMemo - পারফরম্যান্স অপ্টিমাইজেশন

আগের পর্বে আমরা Context API শিখেছিলাম। এই পর্বে আমরা দেখব useMemo — একটা Hook যেটা ভারী গণনার ফলাফল "ক্যাশ" করে রাখে, যাতে অপ্রয়োজনীয় re-render-এ বারবার একই কাজ না করতে হয়।

সমস্যাটা কী? — প্রতিটা Render-এ সব কোড আবার চলে

একটা গুরুত্বপূর্ণ ব্যাপার মনে রাখা দরকার — একটা component re-render হলে, তার পুরো ফাংশন বডি আবার রান হয়, শুধু JSX-এর যেটুকু পাল্টেছে সেটুকু DOM-এ আপডেট হয়। তাই যদি ফাংশনের ভেতরে কোনো ভারী গণনা থাকে, সেটা প্রতিবার re-render হলেই আবার চলবে — এমনকি সেই গণনার সাথে সম্পর্কহীন কোনো state পাল্টালেও।

function ProductList({ products }) {
  const [searchQuery, setSearchQuery] = React.useState("");
  const [isDarkMode, setIsDarkMode] = React.useState(false); // এটা পাল্টালেও নিচের গণনা আবার চলে!

  // ধরো এটা একটা ভারী গণনা — হাজার হাজার প্রোডাক্ট থেকে গড় দাম বের করা
  const averagePrice = products.reduce((sum, p) => sum + p.price, 0) / products.length;

  return (
    <div>
      <p>গড় দাম: ৳{averagePrice}</p>
      <button onClick={() => setIsDarkMode(!isDarkMode)}>থিম টগল</button>
      {/* থিম টগল করলেও averagePrice-এর হিসাব আবার হচ্ছে, যদিও products পাল্টায়নি! */}
    </div>
  );
}

এখানে isDarkMode টগল করলেও averagePrice-এর গণনা আবার চলছে — অথচ products একই আছে, তাই ফলাফলও একই হবে। ছোট অ্যারেতে এটা কোনো সমস্যা না, কিন্তু হাজার হাজার আইটেমে জটিল গণনায় এটা লক্ষণীয় দেরি তৈরি করতে পারে।

useMemo দিয়ে সমাধান

useMemo একটা গণনার ফলাফল "মনে রাখে" — শুধু dependency array-তে দেওয়া ভ্যালু পাল্টালেই আবার গণনা করে, নাহলে আগের ফলাফলই ফেরত দেয়।

function ProductList({ products }) {
  const [searchQuery, setSearchQuery] = React.useState("");
  const [isDarkMode, setIsDarkMode] = React.useState(false);

  const averagePrice = React.useMemo(() => {
    return products.reduce((sum, p) => sum + p.price, 0) / products.length;
  }, [products]); // শুধু products পাল্টালেই আবার হিসাব হবে

  return (
    <div>
      <p>গড় দাম: ৳{averagePrice}</p>
      <button onClick={() => setIsDarkMode(!isDarkMode)}>থিম টগল</button>
      {/* এখন থিম টগল করলে averagePrice আবার গণনা হবে না */}
    </div>
  );
}

সিনট্যাক্সটা useEffect পর্বের dependency array-এর মতোই দেখতে — একটা ফাংশন, আর একটা অ্যারে যেটা বলে দেয় কখন আবার চালাতে হবে।

অংশ কাজ
প্রথম আর্গুমেন্ট (ফাংশন) গণনার কোড — শুধু দরকার হলেই চলবে
দ্বিতীয় আর্গুমেন্ট (অ্যারে) কোন ভ্যারিয়েবল পাল্টালে আবার গণনা হবে
রিটার্ন ভ্যালু গণনার ফলাফল (ক্যাশড অথবা নতুন)

লাইভ ডেমো — useMemo ছাড়া বনাম সহ

নিচে "প্রাইম সংখ্যা গোনা" একটা ইচ্ছাকৃতভাবে ভারী গণনা — থিম টগল বাটনে চাপো, দেখো কোন ভার্সনে গণনা আবার চলছে (কনসোল লগের মতো নিচে দেখানো হচ্ছে)।

checkbox অন থাকলে থিম টগলে গণনা আবার চলবে না, বন্ধ করলে প্রতিবার চলবে — সামান্য ল্যাগও অনুভব করতে পারো বড় limit-এ।

useMemo-এর আরেকটা ব্যবহার — Reference সমতা বজায় রাখা

Advanced JS পর্ব ৫-এ শিখেছিলাম { ...obj }-এর মতো spread করলে একটা নতুন অবজেক্ট তৈরি হয়, যদিও তার ভেতরের মান একই। এটা useEffect dependency-তে সমস্যা তৈরি করতে পারে।

function Dashboard({ userId }) {
  // ❌ সমস্যা — প্রতিবার render হলে filters একটা নতুন অবজেক্ট,
  // যদিও মান একই, তাই এটা dependency-তে থাকলে useEffect প্রতিবার আবার চলবে
  const filters = { userId, active: true };

  React.useEffect(() => {
    fetchData(filters);
  }, [filters]); // এখানে অসীম লুপের ঝুঁকি!
}
function Dashboard({ userId }) {
  // ✅ সমাধান — useMemo দিয়ে userId না পাল্টালে একই object reference থাকবে
  const filters = React.useMemo(() => {
    return { userId, active: true };
  }, [userId]);

  React.useEffect(() => {
    fetchData(filters);
  }, [filters]); // এখন userId পাল্টালেই শুধু চলবে

এখানে useMemo "ক্যাশিং"-এর চেয়েও বেশি কিছু করছে — এটা একটা স্থিতিশীল reference তৈরি করে রাখছে, যাতে userId না পাল্টালে filters-এর reference-ও একই থাকে, আর dependency array-তে এটা "পাল্টায়নি" বলে গণ্য হয়।

কখন useMemo ব্যবহার করবে না

গুরুত্বপূর্ণ সতর্কতা: useMemo নিজেই "ফ্রি" না — এটাও কিছুটা মেমরি আর কম্পিউটেশন খরচ করে (dependency তুলনা করার জন্য)। সাধারণ, দ্রুত গণনায় এটা ব্যবহার করলে লাভের চেয়ে ক্ষতি বেশি হতে পারে।

// ❌ অপ্রয়োজনীয় — এই যোগফল এতই সহজ যে useMemo-এর ওভারহেডই বেশি
const total = React.useMemo(() => a + b, [a, b]);

// ✅ ঠিক আছে — সরাসরি লিখলেই যথেষ্ট
const total = a + b;
ব্যবহার করবে ব্যবহার করবে না
বড় অ্যারে ফিল্টার/সর্ট/ম্যাপ করা সাধারণ যোগ-বিয়োগ, স্ট্রিং জোড়া লাগানো
জটিল গাণিতিক হিসাব (যেমন প্রাইম গোনা) ছোট অ্যারেতে সাধারণ অপারেশন
Object/Array reference স্থিতিশীল রাখা (dependency-তে ব্যবহারের জন্য) যেকোনো ছোট গণনা যেটা মাইক্রোসেকেন্ডে হয়

সহজ নিয়ম: আগে সাধারণভাবে কোড লেখো, তারপর যদি সত্যিই ধীরগতির সমস্যা দেখা যায় (React DevTools দিয়ে মাপা যায়, পর্ব ৬-এ দেখব), তখনই useMemo যোগ করো — "premature optimization" এড়িয়ে চলো।

একটা সম্পূর্ণ উদাহরণ — ফিল্টার করা ও সর্ট করা তালিকা

চলো React এর হাতেখড়ি পর্ব ১৮-এর প্রোডাক্ট লিস্টে useMemo প্রয়োগ করি — সার্চ আর ফিল্টার প্রতিবার সব প্রোডাক্ট থেকে আবার হিসাব না করে, শুধু দরকার হলেই করবে।

function ProductList({ products }) {
  const [searchQuery, setSearchQuery] = React.useState("");
  const [selectedCategory, setSelectedCategory] = React.useState("সব");
  const [sortOrder, setSortOrder] = React.useState("name");

  const filteredAndSorted = React.useMemo(() => {
    console.log("ফিল্টার + সর্ট আবার চলছে"); // dependency পাল্টালেই শুধু দেখা যাবে

    return products
      .filter((p) => selectedCategory === "সব" || p.category === selectedCategory)
      .filter((p) => p.name.includes(searchQuery))
      .sort((a, b) => sortOrder === "price" ? a.price - b.price : a.name.localeCompare(b.name));
  }, [products, searchQuery, selectedCategory, sortOrder]);

  return (
    <div>
      {/* সার্চ/ফিল্টার UI */}
      {filteredAndSorted.map((p) => <div key={p.id}>{p.name}</div>)}
    </div>
  );
}

এখানে dependency array-তে [products, searchQuery, selectedCategory, sortOrder] — এই চারটার যেকোনো একটা পাল্টালেই শুধু ফিল্টার+সর্ট আবার চলবে, অন্য কোনো অসম্পর্কিত state (যেমন একটা মডাল খোলা/বন্ধ) পাল্টালে না।

নিজে চেষ্টা করো

Exercise:

  1. উপরের প্রাইম-গোনা ডেমো নিজে বানাও, limit বাড়িয়ে (যেমন ১ লাখ) useMemo-এর পার্থক্য আরও স্পষ্টভাবে অনুভব করো।
  2. একটা component বানাও যেখানে একটা object useMemo দিয়ে স্থিতিশীল রাখা হচ্ছে, সেটা একটা useEffect-এর dependency হিসেবে ব্যবহার করো, দেখো অসীম লুপ এড়ানো গেল কিনা।
  3. উপরের ফিল্টার+সর্ট উদাহরণটা React এর হাতেখড়ি পর্ব ১৮-এর নিজের প্রোডাক্ট লিস্ট প্রজেক্টে যোগ করো।
  4. ভেবে দেখো — তোমার কোনো প্রজেক্টে (Mobile Khujo-র ফোন লিস্ট ফিল্টার, বা Pathok Ghor-এর বই সার্চ) useMemo প্রয়োগ করলে লাভ হতে পারে কিনা।

পরের পর্বে যা থাকছে

পরের পর্বে আমরা শিখব useCallbackuseMemo-এর "কাজিন" Hook, যেটা মান না কিন্তু ফাংশন ক্যাশ করে, আর কীভাবে এটা child component-এর অপ্রয়োজনীয় re-render কমাতে সাহায্য করে।

একটি মন্তব্য পোস্ট করুন

নবীনতর পূর্বতন

যোগাযোগ ফর্ম