memo আপনাকে একটি কম্পোনেন্টের পুনরায় রেন্ডারিং এড়াতে দেয় যখন এর প্রপস অপরিবর্তিত থাকে।

const MemoizedComponent = memo(SomeComponent, arePropsEqual?)

রেফারেন্স

memo(Component, arePropsEqual?)

একটি কম্পোনেন্টকে memo-তে মোড়ানো হলে এর memoized সংস্করণ পাওয়া যায়। এই মেমোয়াইজড কম্পোনেন্ট সাধারণত তার parent কম্পোনেন্ট পুনরায় রেন্ডার হলেও পুনরায় রেন্ডার করা হয় না, যদি তার প্রপস অপরিবর্তিত থাকে। তবে React তারপরও এটি পুনরায় রেন্ডার করতে পারেঃ মেমোয়াইজেশন একটি পারফরম্যান্স অপ্টিমাইজেশন, গ্যারান্টি নয়।

import { memo } from 'react';

const SomeComponent = memo(function SomeComponent(props) {
// ...
});

নিচে আরো উদাহরণ দেখুন।

প্যারামিটারস

  • Component: যে কম্পোনেন্টটি আপনি মেমোয়াইজ করতে চান। memo এই কম্পোনেন্টকে পরিবর্তন করে না, বরং এর পরিবর্তে একটি নতুন, মেমোয়াইজড কম্পোনেন্ট রিটার্ন করে। যেকোনো বৈধ React কম্পোনেন্ট, ফাংশন এবং forwardRef কম্পোনেন্টসহ, গ্রহণযোগ্য।

  • ঐচ্ছিক arePropsEqual: দুটি আর্গুমেন্ট গ্রহণকারী একটি ফাংশন: কম্পোনেন্টের পূর্ববর্তী প্রপস এবং এর নতুন প্রপস। যদি পুরানো এবং নতুন প্রপস একই হয়ঃ অর্থাৎ, যদি কম্পোনেন্ট নতুন প্রপসের সাথে পুরানো প্রপসের মতোই আউটপুট দেয় এবং আচরণ করে, তাহলে এটি true রিটার্ন করবে। অন্যথায় এটি false রিটার্ন করবে। সাধারণত, আপনি এই ফাংশনটি নির্দিষ্ট করবেন না। ডিফল্ট হিসাবে, React প্রতিটি প্রপকে Object.is-এর সাথে তুলনা করবে।

রিটার্নস

memo একটি নতুন React কম্পোনেন্ট ফেরত দেয়। এটি memo-তে প্রদত্ত কম্পোনেন্টের মতোই আচরণ করে, তবে React সবসময় এটিকে তার প্যারেন্ট পুনরায় রেন্ডার হলে পুনরায় রেন্ডার করবে না, যদি না তার প্রপস পরিবর্তিত হয়।


ব্যবহার

প্রপস অপরিবর্তিত থাকলে রি-রেন্ডারিং এড়ানো

React সাধারণত একটি কম্পোনেন্ট পুনরায় রেন্ডার করে যখন এর প্যারেন্ট পুনরায় রেন্ডার হয়। memo দ্বারা, আপনি এমন একটি কম্পোনেন্ট তৈরি করতে পারেন যা React তার প্যারেন্ট পুনরায় রেন্ডার হলেও পুনরায় রেন্ডার করবে না, যতক্ষণ না এর নতুন প্রপস পুরানো প্রপসের সাথে একই থাকে। এমন একটি কম্পোনেন্টকে মেমোয়াইজড বলা হয়।

একটি কম্পোনেন্টকে মেমোয়াইজ করতে, এটিকে memo-এ wrap korun মোড়ান এবং এর রিটার্ন করা মানটি আপনার মূল কম্পোনেন্টের পরিবর্তে ব্যবহার করুনঃ

const Greeting = memo(function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
});

export default Greeting;

একটি React কম্পোনেন্টের সবসময় pure রেন্ডারিং লজিক থাকা উচিত। এর অর্থ এটি একই আউটপুট ফেরত দেবে যদি এর প্রপস, স্টেট এবং কন্টেক্স্ট পরিবর্তিত না হয়। memo ব্যবহার করে, আপনি React-কে জানাচ্ছেন যে আপনার কম্পোনেন্ট এই প্রয়োজনীয়তাটি পূরণ করে, তাই React-এর প্রপস পরিবর্তিত না হলে পুনরায় রেন্ডার করতে হবে না। memo ব্যবহার করলেও, আপনার কম্পোনেন্ট তার নিজের স্টেট পরিবর্তন হলে অথবা ব্যবহৃত কন্টেক্স্ট পরিবর্তন হলে পুনরায় রেন্ডার করবে।

এই উদাহরণে, লক্ষ্য করুন যে Greeting কম্পোনেন্টটি name পরিবর্তিত হলে পুনরায় রেন্ডার হয় (কারণ এটি এর একটি প্রপ), কিন্তু address পরিবর্তন হলে নয় (কারণ এটি Greeting-এ প্রপ হিসেবে পাস করা হয় না):

import { memo, useState } from 'react';

export default function MyApp() {
  const [name, setName] = useState('');
  const [address, setAddress] = useState('');
  return (
    <>
      <label>
        Name{': '}
        <input value={name} onChange={e => setName(e.target.value)} />
      </label>
      <label>
        Address{': '}
        <input value={address} onChange={e => setAddress(e.target.value)} />
      </label>
      <Greeting name={name} />
    </>
  );
}

const Greeting = memo(function Greeting({ name }) {
  console.log("Greeting was rendered at", new Date().toLocaleTimeString());
  return <h3>Hello{name && ', '}{name}!</h3>;
});

খেয়াল করুন

আপনি কেবল কর্মক্ষমতা অপ্টিমাইজেশন হিসাবে memo-এর উপর নির্ভর করা উচিত। যদি আপনার কোড memo ছাড়া কাজ না করে, তাহলে প্রথমে মৌলিক সমস্যাটি খুঁজে বের করুন এবং তা ঠিক করুন। তারপর আপনি কর্মক্ষমতা উন্নতির জন্য memo যোগ করতে পারেন।

গভীরভাবে জানুন

আপনি কি সর্বত্র memo যোগ করবেন?

যদি আপনার অ্যাপ এই সাইটের মতো হয়, এবং বেশিরভাগ ইন্টারেকশন ভারী (যেমন একটি পৃষ্ঠা বা পুরো অংশ প্রতিস্থাপন করা) হয়, তাহলে মেমোয়াইজেশন সাধারণত অপ্রয়োজনীয়। অন্যদিকে, যদি আপনার অ্যাপ একটি ড্রয়িং এডিটরের মতো হয়, এবং বেশিরভাগ ইন্টারেকশন সূক্ষ্ম (যেমন shape সরানো), তাহলে মেমোয়াইজেশন আপনার কাছে খুব উপকারী মনে হতে পারে।

memo দ্বারা অপ্টিমাইজেশন তখনই মূল্যবান যখন আপনার কম্পোনেন্ট একই নির্দিষ্ট প্রপসের সাথে প্রায়শই পুনরায় রেন্ডার হয়, এবং এর পুনরায় রেন্ডার লজিক খরচবহুল। যদি আপনার কম্পোনেন্ট পুনরায় রেন্ডার হওয়ার সময় কোনো লক্ষণীয় ধীরতা না থাকে, তাহলে memo অপ্রয়োজনীয়। মনে রাখবেন, যদি আপনার কম্পোনেন্টের পাস করা প্রপস সর্বদা আলাদা হয়, যেমন যদি আপনি একটি অবজেক্ট বা রেন্ডারিং সময় ডিফাইন করা একটি সাধারণ ফাংশন পাস করেন। এই কারণে আপনার প্রায়শই useMemo এবং useCallback এর সাথে memo ব্যবহার করা প্রয়োজন হবে।

memo দ্বারা একটি কম্পোনেন্ট wrap করার অন্যান্য ক্ষেত্রে কোনো উপকার নেই। এটি করলে বিশেষ কোনো ক্ষতি হয় না, তাই কিছু টিম প্রতিটি ক্ষেত্রে ভেবে না দেখে যথাসম্ভব মেমোয়াইজ করতে চায়। এই পদ্ধতির নেতিবাচক দিক হল কোড কম পাঠযোগ্য হয়ে যায়। এছাড়াও, সব মেমোয়াইজেশন কার্যকর নয়ঃ একটি “সর্বদা নতুন” মান পুরো কম্পোনেন্টের মেমোয়াইজেশন ভেঙ্গে দেবার জন্য যথেষ্ট।

ব্যবহারিকভাবে, কয়েকটি নীতি অনুসরণ করে আপনি ক্ষেত্রবিশেষে অনেক মেমোয়াইজেশন অপ্রয়োজনীয় করে তুলতে পারেনঃ

  1. যখন একটি কম্পোনেন্ট অন্যান্য কম্পোনেন্টগুলিকে দৃশ্যত জড়িয়ে রাখে, তখন এটিকে JSX চাইল্ড হিসাবে গ্রহণ করতে দিন। এর ফলে, যখন র‍্যাপার কম্পোনেন্ট নিজের স্টেট আপডেট করে, React জানে যে এর চাইল্ডগুলিকে পুনরায় রেন্ডার করার প্রয়োজন নেই।
  2. স্থানীয় স্টেটকে প্রাধান্য দিন এবং স্টেট উপরে তোলা প্রয়োজনের চেয়ে বেশি না করা ভালো। উদাহরণস্বরূপ, ফর্ম এবং কোনো আইটেম উপরে হোভার করা নিয়ে অস্থায়ী স্টেট আপনার ট্রির শীর্ষে বা একটি গ্লোবাল স্টেট লাইব্রেরিতে রাখবেন না।
  3. আপনার রেন্ডারিং লজিক শুদ্ধ রাখুন। যদি কোনো কম্পোনেন্ট পুনরায় রেন্ডার করা সমস্যা সৃষ্টি করে বা কোনো লক্ষণীয় দৃশ্য সমস্যা তৈরি করে, তাহলে এটি আপনার কম্পোনেন্টের বাগ! মেমোয়াইজেশন যোগ করার পরিবর্তে বাগটি ঠিক করুন।
  4. অপ্রয়োজনীয় ইফেক্ট যা স্টেট আপডেট করে এড়িয়ে চলুন। বেশিরভাগ React অ্যাপের কর্মক্ষমতা সমস্যা এমন আপডেট চেইন থেকে উদ্ভূত হয় যা ইফেক্ট থেকে আসে এবং আপনার কম্পোনেন্টগুলিকে বারবার রেন্ডার করায়।
  5. আপনার ইফেক্ট থেকে অপ্রয়োজনীয় নির্ভরতা সরিয়ে ফেলার চেষ্টা করুন। উদাহরণস্বরূপ, মেমোয়াইজেশনের পরিবর্তে, কোনো অবজেক্ট বা ফাংশনকে একটি ইফেক্টের মধ্যে বা কম্পোনেন্টের বাইরে সরান৤

যদি কোনো নির্দিষ্ট ইন্টারেকশন এখনও ধীর অনুভূত হয়, React Developer Tools প্রোফাইলার ব্যবহার করুন যেগুলি কম্পোনেন্টগুলি মেমোয়াইজেশন থেকে সর্বাধিক উপকার পাবে তা দেখার জন্য, এবং প্রয়োজনমতো মেমোয়াইজেশন যোগ করুন। এই নীতিগুলি আপনার কম্পোনেন্টগুলিকে ডিবাগ করা এবং বোঝা সহজ করে তোলে, তাই যেকোনো অবস্থায় এগুলি অনুসরণ করা ভালো। দীর্ঘমেয়াদে, আমরা স্বয়ংক্রিয়ভাবে সূক্ষ্ম মেমোয়াইজেশন করা নিয়ে গবেষণা করছি যা এই সমস্যাটি একবারের জন্য সমাধান করবে।


স্টেট ব্যবহার করে মেমোয়াইজড কম্পোনেন্ট আপডেট করা

যদিও একটি কম্পোনেন্ট মেমোয়াইজ করা হয়, এটি তবুও পুনরায় রেন্ডার হবে যখন এর নিজের স্টেট বদলায়। মেমোয়াইজেশন শুধুমাত্র প্যারেন্ট কম্পোনেন্ট থেকে পাস করা প্রপস নিয়ে কাজ করে।

import { memo, useState } from 'react';

export default function MyApp() {
  const [name, setName] = useState('');
  const [address, setAddress] = useState('');
  return (
    <>
      <label>
        Name{': '}
        <input value={name} onChange={e => setName(e.target.value)} />
      </label>
      <label>
        Address{': '}
        <input value={address} onChange={e => setAddress(e.target.value)} />
      </label>
      <Greeting name={name} />
    </>
  );
}

const Greeting = memo(function Greeting({ name }) {
  console.log('Greeting was rendered at', new Date().toLocaleTimeString());
  const [greeting, setGreeting] = useState('Hello');
  return (
    <>
      <h3>{greeting}{name && ', '}{name}!</h3>
      <GreetingSelector value={greeting} onChange={setGreeting} />
    </>
  );
});

function GreetingSelector({ value, onChange }) {
  return (
    <>
      <label>
        <input
          type="radio"
          checked={value === 'Hello'}
          onChange={e => onChange('Hello')}
        />
        Regular greeting
      </label>
      <label>
        <input
          type="radio"
          checked={value === 'Hello and welcome'}
          onChange={e => onChange('Hello and welcome')}
        />
        Enthusiastic greeting
      </label>
    </>
  );
}

যদি আপনি একটি স্টেট ভেরিয়েবলকে এর বর্তমান মানে সেট করেন, React আপনার কম্পোনেন্টের পুনরায় রেন্ডার করা এড়িয়ে যাবে, এমনকি memo ছাড়াও। আপনি হয়তো দেখতে পাবেন যে আপনার কম্পোনেন্ট ফাংশনটি অতিরিক্ত সময় কল করা হচ্ছে, কিন্তু ফলাফলটি বাতিল করা হবে।


একটি কনটেক্সট ব্যবহার করে মেমোয়াইজড কম্পোনেন্ট আপডেট করা

যদিও একটি কম্পোনেন্ট মেমোয়াইজ করা হয়, এটি তবুও পুনরায় রেন্ডার হবে যখন এর ব্যবহার করা কনটেক্সট পরিবর্তন হয়। মেমোয়াইজেশন শুধুমাত্র অভিভাবক কম্পোনেন্ট থেকে পাস করা প্রপস নিয়ে কাজ করে।

import { createContext, memo, useContext, useState } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  const [theme, setTheme] = useState('dark');

  function handleClick() {
    setTheme(theme === 'dark' ? 'light' : 'dark'); 
  }

  return (
    <ThemeContext.Provider value={theme}>
      <button onClick={handleClick}>
        Switch theme
      </button>
      <Greeting name="Taylor" />
    </ThemeContext.Provider>
  );
}

const Greeting = memo(function Greeting({ name }) {
  console.log("Greeting was rendered at", new Date().toLocaleTimeString());
  const theme = useContext(ThemeContext);
  return (
    <h3 className={theme}>Hello, {name}!</h3>
  );
});

আপনার কম্পোনেন্টকে কেবল তখনই পুনরায় রেন্ডার করতে দিন যখন কোনো কনটেক্সটের একটি অংশ পরিবর্তন হয়, আপনার কম্পোনেন্টকে দুই ভাগে ভাগ করুন। বাইরের কম্পোনেন্টে কনটেক্সট থেকে প্রয়োজনীয় তথ্য পড়ুন এবং এটি একটি মেমোয়াইজড চাইল্ডে প্রপ হিসেবে পাস করুন।


প্রপস পরিবর্তন সর্বনিম্ন করা

memo ব্যবহার করলে, আপনার কম্পোনেন্ট পুনরায় রেন্ডার হয় যখন যেকোনো প্রপ আগের থেকে shallowly equal নয়। এর অর্থ হলো React আপনার কম্পোনেন্টের প্রতিটি প্রপকে এর আগের মানের সাথে Object.is comparison ব্যবহার করে তুলনা করে। লক্ষ্য করুন যে Object.is(3, 3) true হলেও, Object.is({}, {}) false

memo থেকে সর্বাধিক উপকার পেতে, প্রপস পরিবর্তনের সংখ্যা সর্বনিম্ন করুন। উদাহরণস্বরূপ, যদি প্রপটি একটি অবজেক্ট হয়, তাহলে প্যারেন্ট কম্পোনেন্ট প্রতিবার সেই অবজেক্টটি পুনরায় তৈরি করা থেকে বিরত থাকতে useMemo ব্যবহার করুনঃ

function Page() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(42);

const person = useMemo(
() => ({ name, age }),
[name, age]
);

return <Profile person={person} />;
}

const Profile = memo(function Profile({ person }) {
// ...
});

প্রপস পরিবর্তন আরও কমানোর ভালো উপায় হলো নিশ্চিত করা যে কম্পোনেন্টটি তার প্রপসে কেবল সর্বনিম্ন প্রয়োজনীয় তথ্য গ্রহণ করে। উদাহরণস্বরূপ, এটি একটি পুরো অবজেক্টের পরিবর্তে পৃথক মানগুলি গ্রহণ করতে পারেঃ

function Page() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(42);
return <Profile name={name} age={age} />;
}

const Profile = memo(function Profile({ name, age }) {
// ...
});

এমনকি পৃথক মানগুলিও কখনও কখনও কম পরিবর্তিত হওয়া মানগুলিতে project করা যেতে পারে। উদাহরণস্বরূপ, এখানে একটি কম্পোনেন্ট একটি মানের উপস্থিতি নির্দেশ করা একটি বুলিয়ান গ্রহণ করেঃ

function GroupsLanding({ person }) {
const hasGroups = person.groups !== null;
return <CallToAction hasGroups={hasGroups} />;
}

const CallToAction = memo(function CallToAction({ hasGroups }) {
// ...
});

যখন আপনার মেমোয়াইজড কম্পোনেন্টে একটি ফাংশন পাস করতে হবে, তখন এটি হয় আপনার কম্পোনেন্টের বাইরে declare করুন যাতে এটি কখনই পরিবর্তিত না হয়, অথবা এর definition পুনরায় রেন্ডারের মধ্যে cache করতে useCallback ব্যবহার করুন।


কাস্টম তুলনা ফাংশন নির্দিষ্ট করা

বিরল ক্ষেত্রে মেমোয়াইজড কম্পোনেন্টের প্রপস পরিবর্তন সর্বনিম্ন করা অসম্ভব হতে পারে। সেক্ষেত্রে, আপনি একটি কাস্টম তুলনা ফাংশন দিতে পারেন, যা React পুরানো এবং নতুন প্রপসের তুলনা করতে ব্যবহার করবে shallow equality এর পরিবর্তে। এই ফাংশনটি memo-র দ্বিতীয় আর্গুমেন্ট হিসেবে পাস করা হয়। এটি কেবল true ফেরত দেবে যদি নতুন প্রপস পুরানো প্রপসের মতোই আউটপুট তৈরি করে; অন্যথায় এটি false ফেরত দেবে।

const Chart = memo(function Chart({ dataPoints }) {
// ...
}, arePropsEqual);

function arePropsEqual(oldProps, newProps) {
return (
oldProps.dataPoints.length === newProps.dataPoints.length &&
oldProps.dataPoints.every((oldPoint, index) => {
const newPoint = newProps.dataPoints[index];
return oldPoint.x === newPoint.x && oldPoint.y === newPoint.y;
})
);
}

এটি করলে, আপনার ব্রাউজার ডেভেলপার টুলসের পারফরম্যান্স প্যানেল ব্যবহার করে নিশ্চিত করুন যে আপনার comparison ফাংশনটি কম্পোনেন্টের পুনরায় রেন্ডার করার চেয়ে দ্রুত হচ্ছে। আপনি অবাক হয়ে যেতে পারেন।

যখন আপনি পারফরম্যান্স পরিমাপ করবেন, নিশ্চিত করুন যে React প্রোডাকশন মোডে চলছে।

সতর্কতা

যদি আপনি কাস্টম arePropsEqual implementation সরবরাহ করেন, আপনাকে প্রতিটি প্রপ, ফাংশনসহ তুলনা করতে হবে। ফাংশনগুলি প্রায়শই প্যারেন্ট কম্পোনেন্টের প্রপস এবং স্টেটের উপর ক্লোজ করে। যদি আপনি oldProps.onClick !== newProps.onClick হলে true ফেরত দেন, আপনার কম্পোনেন্ট তার onClick হ্যান্ডলারে আগের রেন্ডারের প্রপস এবং স্টেট দেখতে থাকবে, যা খুব বিভ্রান্তিকর বাগের জন্ম দেয়।

arePropsEqual-এ deep equality চেক করা এড়িয়ে চলুন, যদি না আপনি শতভাগ নিশ্চিত হন যে আপনি যে ডেটা স্ট্রাকচার নিয়ে কাজ করছেন তার একটি পরিচিত সীমিত গভীরতা রয়েছে। Deep equality চেক অত্যন্ত ধীর হতে পারে এবং যদি কেউ পরে ডেটা স্ট্রাকচার পরিবর্তন করে তাহলে আপনার অ্যাপটি অনেক সেকেন্ড ধরে আটকে থাকতে পারে।


ট্রাবলশুটিং

আমার কম্পোনেন্ট পুনরায় রেন্ডার হয় যখন একটি প্রপ অবজেক্ট, অ্যারে, অথবা ফাংশন হয়

React পুরানো এবং নতুন প্রপসগুলি shallow equality দ্বারা তুলনা করেঃ অর্থাৎ, এটি বিবেচনা করে যে প্রতিটি নতুন প্রপ পুরানো প্রপের সাথে রেফারেন্স-সমান কিনা। যদি আপনি প্রতি বার প্যারেন্ট পুনরায় রেন্ডার হলে একটি নতুন অবজেক্ট বা অ্যারে তৈরি করেন, এমনকি যদি প্রতিটি উপাদান একই হয়, React এটিকে পরিবর্তিত হিসেবে বিবেচনা করবে। একই ভাবে, যদি আপনি প্যারেন্ট কম্পোনেন্ট রেন্ডার করার সময় একটি নতুন ফাংশন তৈরি করেন, React এটিকে পরিবর্তিত হিসেবে বিবেচনা করবে, এমনকি যদি ফাংশনের ডেফিনিশন একই হয়। এটি এড়াতে, প্রপসগুলি সরল করুন অথবা প্যারেন্ট কম্পোনেন্টে প্রপসগুলি মেমোয়াইজ করুন