React চায় আপনার কম্পোনেন্টগুলো যেন প্রেডিক্টেবল (predictable) হয়। একই state এবং props দিলে এটি প্রতিবার একই UI রেন্ডার করবে। কিন্তু বেশিরভাগ বাস্তব অ্যাপ্লিকেশন এই সীমাবদ্ধতার মধ্যে টিকে থাকতে পারে না। তাদের বাইরের জগতের সাথে যোগাযোগ করতে হয়। একটি ড্যাশবোর্ডের জন্য সার্ভার থেকে নতুন ডেটা প্রয়োজন। একটি চ্যাট উইজেটের জন্য মেসেজ শোনার প্রয়োজন হয়। একটি টাইমারের জন্য টিক করা প্রয়োজন। এই অপারেশনগুলো হলো side effects, এবং এগুলো React-এর render cycle-এর বাইরে থাকে। useEffect হুক হলো সেই জায়গা যেখানে আপনি এই অগোছালো এবং অনিশ্চিত কাজগুলো রাখেন যাতে আপনার কম্পোনেন্টটি নিজে সঠিক ও নির্ভরযোগ্য থাকে।
Side Effects: কী কী useEffect-এর ভেতরে থাকা উচিত
একটি side effect হলো JSX রিটার্ন করা ছাড়াও অন্য যেকোনো কিছু যা বাইরের জগতের সাথে যোগাযোগ করে। React-এর render phase হওয়া উচিত pure। যখন আপনি ডেটা ফেচ করা, গ্লোবাল ভেরিয়েবলে কিছু লেখা, বা DOM-এ লিসেনার যুক্ত করার মতো কাজ শুরু করেন, তখন আপনি pure territory থেকে বেরিয়ে যান।
সাধারণ কিছু উদাহরণ হলো:
- API থেকে ডেটা ফেচ করা
- টাইমার বা ইন্টারভাল সেট করা
windowবাdocument-এ event listener যোগ করা- ব্রাউজার ট্যাবের টাইটেল আপডেট করা
- WebSockets-এর সাথে কানেক্ট করা
এই কাজগুলোর একটি সাধারণ বৈশিষ্ট্য রয়েছে: এগুলো আপনার কম্পোনেন্টের return statement বা মূল render logic-এর অংশ নয়। রেন্ডার বডির ভেতরে সরাসরি setInterval-এর মতো ব্রাউজার API কল করার চেষ্টা করলে তা প্রতিবার রেন্ডারের সময় চলতে থাকবে, যা ডুপ্লিকেট টাইমার তৈরি করবে এবং বিভ্রান্তিকর আচরণ করবে। useEffect ঠিক এই কাজটিকে আলাদা করতে এবং সঠিক সময়ে এটি চালানোর জন্যই তৈরি করা হয়েছে।
Dependency Array কীভাবে টাইমিং নিয়ন্ত্রণ করে
useEffect-এর দ্বিতীয় আর্গুমেন্ট হলো dependency array, যা ক্লাস কম্পোনেন্ট থেকে আসা ডেভেলপারদের জন্য বিভ্রান্তির সবচেয়ে বড় কারণ। এটিকে ভেরিয়েবলের একটি সেট হিসেবে ভাবুন যা React পর্যবেক্ষণ করে সিদ্ধান্ত নেয় যে বর্তমান রেন্ডারের পর আপনার effect-টি স্কিপ করবে নাকি এক্সিকিউট করবে।
এখানে তিনটি প্যাটার্ন রয়েছে যা আপনি বারবার ব্যবহার করবেন।
কোনো dependency array না থাকা। আপনি যদি অ্যারেটি পুরোপুরি বাদ দেন, তবে React ধরে নেবে যে আপনি চান effect-টি প্রথম রেন্ডারসহ প্রতিবার রেন্ডারের পর চলুক। এটি খুব কম ক্ষেত্রেই প্রয়োজন হয়। যদি আপনার effect-টি কোনো নেটওয়ার্ক রিকোয়েস্ট বা ভারী DOM অপারেশন করে থাকে, তবে প্রতিবার কি-স্ট্রোক বা স্টেট পরিবর্তনের সময় এটি চালানো পারফরম্যান্স কমিয়ে দেবে। এই প্যাটার্নটি কেবল তখনই ব্যবহার করুন যখন আপনার সত্যিই কিছু পুনরায় চালানোর প্রয়োজন হয় এবং আপনি নির্দিষ্ট করে বলতে পারেন না কোন prop বা state পরিবর্তিত হয়েছে।
একটি খালি অ্যারে []। এটি React-কে নির্দেশ দেয় যে কম্পোনেন্টটি মাউন্ট হওয়ার এবং DOM প্রস্তুত হওয়ার ঠিক পরেই একবার effect-টি চালাতে। প্রাথমিক ডেটা ফেচ করার জন্য এটি সঠিক জায়গা। উদাহরণস্বরূপ, যদি আপনার কম্পোনেন্ট ইউজার প্রোফাইল ডেটা লোড করে, তবে আপনি চাইবেন প্রোফাইল পেজটি আসার ঠিক একবারই রিকোয়েস্টটি চলুক, প্রতিবার ইউজার পেজের নিচের কোনো ফর্মে ইন্টারঅ্যাক্ট করার সময় নয়।
নির্দিষ্ট ভেরিয়েবলসহ একটি অ্যারে [count]। এটি একটি নিখুঁত টুল। React এই ডিপেন্ডেন্সিগুলোর বর্তমান মানের সাথে শেষ রেন্ডারের মানের তুলনা করে। যদি তাদের মধ্যে কোনোটি পরিবর্তিত হয়, তবে effect-টি চলে। যদি তালিকার কিছু না বদলায়, তবে React পুরো effect-টি স্কিপ করে।
আপনি যদি ব্রাউজার ট্যাবের টাইটেলকে একটি state ভেরিয়েবলের সাথে সিঙ্ক করতে চান, তবে সেই ভেরিয়েবলটি dependency array-তে রাখতে হবে। তাহলে React কেবল তখনই টাইটেল আপডেট করবে যখন সেই ভেরিয়েবলের মান পরিবর্তন হবে। এটি বাদ দিলে টাইটেলটি পুরনো (stale) থেকে যাবে। আর অপ্রাসঙ্গিক state ভেরিয়েবল দিলে, আপনি এমন পরিবর্তনের জন্য টাইটেল আপডেট করে সাইকেল নষ্ট করবেন যা গুরুত্বপূর্ণ নয়।
Cleanup করা ঐচ্ছিক নয়
কিছু effect পেছনে ছাপ রেখে যায়। একটি টাইমার চলতে থাকে। একটি event listener কাজ করতে থাকে। একটি WebSocket খোলা থাকে। যখন আপনার কম্পোনেন্টটি unmount হয়, বা এমনকি যখন ডিপেন্ডেন্সি পরিবর্তনের কারণে একটি effect পুনরায় চলে, React স্বয়ংক্রিয়ভাবে আগের effect-এর অবশিষ্টাংশ পরিষ্কার করে না। এটি আপনার কাজ।
আপনি useEffect-এর ভেতর থেকে একটি ফাংশন রিটার্ন করার মাধ্যমে একটি cleanup ফাংশন তৈরি করতে পারেন। React পরবর্তী effect প্রয়োগ করার আগে এবং কম্পোনেন্টটি স্ক্রিন থেকে চলে যাওয়ার সময় এই cleanup ফাংশনটি কল করে।
আপনার cleanup ব্যবহার করা উচিত:
clearIntervalবাclearTimeoutদিয়ে ইন্টারভাল বা টাইমআউট ক্লিয়ার করতেwindow,document, বা এক্সটার্নাল নোড-এ যোগ করা event listener সরিয়ে ফেলতে- ডেটা স্ট্রিম বা সার্ভিস থেকে আনসাবস্ক্রাইব (Unsubscribe) করতে
এটি অবহেলা করলে আপনি memory leak-এর সম্মুখীন হবেন। একটি কম্পোনেন্ট মাউন্ট হলো, একটি scroll listener যুক্ত হলো, তারপর কম্পোনেন্টটি unmount হলো, কিন্তু লিসেনারটি থেকে গেল। ব্রাউজার সেই কলব্যাক এবং এর রেফারেন্স করা DOM নোডগুলোকে ধরে রাখে। সময়ের সাথে সাথে, বিশেষ করে ভারী নেভিগেশন সম্পন্ন single-page অ্যাপ্লিকেশনগুলোতে, এই অবশিষ্টাংশগুলো জমা হতে থাকে এবং ট্যাবটিকে স্লো করে দেয়। এর সমাধান সাধারণত মাত্র কয়েক লাইন: একটি ফাংশন রিটার্ন করুন যা আপনার যোগ করা জিনিসটি সরিয়ে ফেলে।
প্রোডাকশনে চলে যাওয়া সাধারণ ভুলসমূহ
এমনকি অভিজ্ঞ ডেভেলপাররাও সহজতর বিকল্প থাকা সত্ত্বেও useEffect ব্যবহার করতে যান। এখানে এমন তিনটি প্যাটার্ন দেওয়া হলো যা কোড রিভিউ করার সময় সতর্ক সংকেত (red flag) হিসেবে দেখা উচিত।
Infinite loops. useEffect-এর ভেতরে কখনোই কোনো state variable আপডেট করবেন না যদি সেই একই variable আপনার dependency array-তে থাকে, যদি না আপনার কাছে এমন কোনো গেট কন্ডিশন (gate condition) থাকে যা এই চক্রটি ভেঙে দেয়। আপনি যদি count পড়েন, সেটি ইনক্রিমেন্ট করেন এবং count-কে dependency হিসেবে তালিকাভুক্ত করেন, তবে React পরিবর্তনটি দেখতে পায়, পুনরায় রেন্ডার করে, ইফেক্টটি আবার চালায়, আবারও ইনক্রিমেন্ট করে এবং ব্রাউজারটি লক করে ফেলে।
Unnecessary effects. বিদ্যমান props বা state থেকে কোনো ভ্যালু ক্যালকুলেট করার জন্য useEffect ব্যবহার করবেন না। আপনি যদি রেন্ডারিংয়ের সময় সরাসরি এটি ডেরিভ (derive) করতে পারেন, তবে সেটিই করুন। ডেরিভড ভ্যালুগুলো (Derived values) কম্পোনেন্ট বডি অথবা useMemo দিয়ে করা একটি মেমোইজড ক্যালকুলেশনের (memoized calculation) অন্তর্ভুক্ত হওয়া উচিত। এগুলোকে একটি ইফেক্টের মধ্যে নিয়ে আসা কোনো লাভ ছাড়াই আপনার লজিককে রেন্ডার এবং ইফেক্ট ফেজের মধ্যে ভাগ করে দেয় এবং কোড বোঝা কঠিন করে তোলে।
The wrong tool for user actions. `use
