সহজ বাংলায় React Hooks সিরিজ। পর্ব ৬ – React Memo, useCallback

সহজ বাংলায় React Hooks সিরিজ। পর্ব ৬ – React Memo, useCallback

আমরা আগের পর্বগুলোতে useStateuseEffectuseRefuseReducer, useContext নিয়ে আলোচনা করেছি। আজকের পর্বে আমরা useCallback এবং React.memo নিয়ে আলোচনা করবো ইনশাল্লাহ। প্রথমেই বলে রাখা ভালো, এগুলো মূলত আমাদের React এর perfomance optimization এ কাজে লাগে।

এগুলো নিয়ে আলোচনার পূর্বে আগে দেখে নেওয়া দরকার কি প্রবলেমের কারনে এগুলোর দরকার হয়। এটাকে বুঝানোর জন্যে একটি ডেমো অ্যাপ বানিয়েছি। দেখা যাক এটার মধ্যে কি আছে।

initial app

আমরা দেখতে পারছি আমাদের অ্যাপে একটি প্যারেন্ট কম্পোনেন্ট আছে যেখানে “Parent Increment Button” নামে একটি বাটন আমাদের প্যারেন্টেই আছে এবং সাথে ৩টি child কম্পোনেন্ট আছে, যেগুলো প্যারেন্ট কম্পোনেন্ট থেকে আলাদা ভাবে আছে।

parent component

এরপরে আমরা দেখি আমাদের child component গুলোতে কি আছে। child component গুলো খুবই সিম্পেল বাটন, একি styling কিন্তু ভিন্ন ভিন্ন backgroundColor, যাতে তাদের আলাদা চিহ্নিত করা যায়। এখানে একটি ইম্পরট্যান্ট জিনিস খেয়াল করি আমরা, প্রতিটি কম্পোনেন্টের প্রথমে আমরা console.log() ব্যাবহার করেছি যাতে আমরা বুঝতে পারি আমাদের child component কখন render এবং re-render হচ্ছে।

child components

আমাদের ইনিশিয়াল সেটআপ শেষ। এখন আমরা কয়েকটি এক্সপেরিমেন্ট যাবো। আমরা আগেই দেখেছি আমাদের প্যারেন্ট কম্পোনেন্টে একটি বাটন আছে এবং এই বাটনটি ক্লিক করলে আমরা আমাদের প্যারেন্ট কম্পোনেন্টের ইন্টারনাল state count এর value change করছি এবং আমরা জানি ইন্টারনাল ষ্টেট আপডেট করলে পুরো কম্পোনেন্ট কিন্তু re-render হয়।

React এর render আমরা যত কম রাখতে পারব আমাদের পারফর্মেন্স ততো ভালো হবে। এখন এই উদাহরণে আমরা দেখছি যে count এর value আপডেট হলে আমাদের FirstChild, SeondChild, ThirdChild সবগুলোই আবার render হচ্ছে অথচ count এর সাথে এদের কোন সম্পর্ক নেই। কাজেই আমরা এখানে বলতে পারি যে আমরা unnecessary আমাদের কম্পোনেন্টকে render করছি। নিচের ভিডিওটি দেখলে ব্যাপারটি আরও ক্লিয়ার হবে।

without memo

আমরা চাচ্ছি যে আমরা যখন প্যারেন্ট কম্পোনেন্টের counter state কে change করবো তখন শুধুমাত্র আমাদের প্যারেন্ট কম্পোনেন্ট re-render হবে কিন্তু child কম্পোনেন্টে কোন আপডেট হবে না কারণ child components গুলো প্যারেন্টের state এর উপর নির্ভরশীল নয়। এটা আমরা কিভাবে করতে পারি? খুব সহজ। আমরা React.memo দিয়ে যদি আমাদের কম্পোনেন্টকে wrap করে দেই তাহলে সেটি আর আপডেট হবে না, তখন আমাদের child component শুধুমাত্র যদি নিজস্ব props অথবা state পরিবর্তন হয় তখনি কেবল re-render হবে। আমরা তাহলে এখন আমাদের FirstChild কে React Memo দ্বারা wrap করে দেখি।

const FirstChild = React.memo(() => {
  console.log('FirstChild');
  return (
    <TouchableOpacity onPress={onPress} style={[styles.child, styles.firstChild]}>
      <Text style={styles.childText}>First Child</Text>
    </TouchableOpacity>
  )
});

নিচের ভিডিওতে আমরা দেখতে পারব এখন প্যারেন্ট কম্পোনেন্টের বাটন ক্লিক করে count update করা সত্ত্বেও আমাদের FirstChild re-render হচ্ছে না কিন্তু SecondChild and ThirdChild উভয়েই re-render হচ্ছে কারণ তাদেরকে React.memo দ্বারা wrap করা হয়নি।

আমরা তাহলে এখন SecondChild and ThirdChild কে একিভাবে memo দিয়ে wrap করে দিতে পারি। সেক্ষেত্রে তারাও আর re-render হবে না।

আমরা তাহলে একটি সমস্যা দুর করলাম। এখন আশা যাক আরেক ধরনের প্রবলেমে। ধরেন আমাদের প্যারেন্ট কম্পোনেন্ট থেকে একটি callback function আমরা আমাদের FirstChild এ props হিসেবে পাস করলাম এবং আমরা প্যারেন্ট কম্পোনেন্ট থেকে আমাদের FirstChild এ একটি counter value update করতে চাচ্ছি। আগে দেখে নেয়া যাক কোডে,

export default function App() {
  const [count, setCount] = useState(0)
  const [firstChildCount, setFirstChildCount] = useState(1)
  ................... remove other previous code
  
return (
    <View style={styles.container}>
      <FirstChild count={firstChildCount} onPress={onPressFirstChild} />
............................................

আমাদের FirstChild এ তাহলে এই props গুলো ধরা লাগবে।

const FirstChild = React.memo(({onPress, count}) => {
  console.log('FirstChild');
  return (
    <TouchableOpacity onPress={onPress} style={[styles.child, styles.firstChild]}>
      <Text style={styles.childText}>First Child = {count}</Text>
    </TouchableOpacity>
  )
});

ওকে, আমরা ফিরে যাই তাহলে আবার আগের সেই এক্সপেরিমেন্টে, খেয়াল করি console.log এর দিকে।

এখন দেখা যাচ্ছে যে React Memo ব্যাবহার করা সত্ত্বেও আমাদের FirstChild re-render হচ্ছে। এর কারণ কি? কারণ হচ্ছে প্রতিবার render এ আমরা onPressFirstChild ফাংশনের একটি নতুন reference বানাচ্ছি এবং আমাদের FirstChild মনে করছে এটি একটি নতুন props, তাই সে নিজেকে আবার render করছে।

এই সমস্যা আমরা কিভাবে এখন রিমুভ করতে পারি? এখানেই আসবে আমাদের useCallback hook. useCallback কি? ডকুমেন্টেশন থেকে আমরা পাই,

useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders

React documentation on useCallback

মানে হচ্ছে useCallback আমাদেরকে callback এর একটি memoized version রিটার্ন করে। memoized version বলতে আমরা কি বুঝাচ্ছি? মানে এটি হচ্ছে আমাদের এটি cached result দেয়। useCallback ফাংশনও useEffect এর মত একটি array of dependencies input একসেপ্ট করে এবং সাথে একটি ইনলাইন ফাংশন। সুতরাং এই ইনলাইন ফাংশনটি তখনি নতুনভাবে তৈরি হয় যখন dependencies input এর কোন ইনপুট চেঞ্জ হয় এর নাহলে এটি নতুন ভাবে আর তৈরি হয় না এবং যার কারণে আমরা অযথা re-render কে avoid করতে পারি।

সুতরাং আমরা এখন আমাদের onPressFirstChild ফাংশনকে useCallback এ পাস করতে পারি এবং সাথে dependencies input এ আমরা [firstChildCount] পাস করতে পারি। আমরা তখনই onPressFirstChild নতুন রেফারেন্স বানাবো যখন firstChildCount value আপডেট হবে। তাঁর মানে কি দাঁড়াচ্ছে? আমরা এখন প্যারেন্ট কম্পোনেন্টে re-render করলে আমাদের FirstChildComponent আর render হবে না কারণ সে নতুন কোন unnecessary props/state পাবে না।

const onPressFirstChild = useCallback(() => {
  setFirstChildCount(firstChildCount + 1)
}, [firstChildCount])
with useCallback

উপড়ের ভিডিওতে আমরা দেখতে পারব এখন যে প্যারেন্ট কম্পোনেন্টের internal state change করার পরে আমরা এখন আর আমাদের FirstChild কে render করছি না কারণ FirstChild এ যে আমরা যে ফাংশন onPressFirstChild পাঠাচ্ছি , তা এখন useCallback এর মধ্যে এবং সেটি নতুনভাবে তখনি কল হবে যখন firstChildCount ষ্টেট আপডেট হবে।

এটিই হচ্ছে সংক্ষেপে useCallback এর ব্যাখ্যা। মূলত পারফর্মেন্স অপটিমাইজেশন এর জন্যে আমরা এটি ব্যাবহার করে থাকি। সচারচর এটার দরকার হয় না কিন্তু যখন আপনার প্যারেন্ট কম্পোনেন্ট অনেক child component কে নিয়ে থাকে তখন আমাদের কোনটি কতবার render হচ্ছে সেই বিষয়ে জ্ঞান থাকা জরুরি নাহলে কমপ্লেক্স কম্পোনেন্টগুলোকে যদি আমরা বার বার render করি যেখানে তাদের re-render এর কোন দরকার নেই সেইখেত্রে আমাদের React app slow হয়ে যাবে।

সিরিজের পরের পর্বগুলো ইনশাল্লাহ –

  • useMemo
  • custom hooks

Source: React Documentation

1 Comment

  1. Software Engineer

    Very detail information

Leave a Reply

Your email address will not be published. Required fields are marked *