আগের পর্বে ফর্ম ও ডেটা হ্যান্ডলিং শেষ হয়েছে। এখন শুরু হচ্ছে "Component যোগাযোগ ও Lifecycle" — প্রথম টপিক useEffect Hook, যেটা দিয়ে একটা component-এ "সাইড ইফেক্ট" চালানো যায় — যেমন কিছু জিনিস component স্ক্রিনে আসার সাথে সাথে করা, বা কোনো ডেটা পাল্টালে প্রতিক্রিয়া দেখানো।
Side Effect কী?
Side Effect বলতে বোঝায় এমন কোনো কাজ যেটা সরাসরি JSX রেন্ডার করার অংশ না — যেমন API কল করা, timer সেট করা, event listener যোগ করা, localStorage-এ কিছু সেভ করা (Advanced HTML সিরিজের পর্ব ১ মনে আছে?), বা document title পাল্টানো। এগুলো component-এর "মূল কাজ" (UI দেখানো) থেকে আলাদা, তাই এদের জন্য আলাদা একটা Hook দরকার — useEffect।
বেসিক সিনট্যাক্স
import { useEffect } from "react";
function MyComponent() {
useEffect(() => {
console.log("এই কোড component রেন্ডার হওয়ার পর চলবে");
});
return <h1>হ্যালো</h1>;
}
useEffect একটা ফাংশন নেয়, যেটা React JSX রেন্ডার করার পরে চালায় — কারণ UI আগে স্ক্রিনে দেখানো হয়, তারপর সাইড ইফেক্ট চলে, যাতে ব্যবহারকারী কোনো দেরি অনুভব না করে।
Dependency Array — কখন আবার চলবে
useEffect-এর দ্বিতীয় আর্গুমেন্ট হলো একটা অ্যারে, যেটা নির্ধারণ করে effect কখন আবার রান হবে। এটাই useEffect-এর সবচেয়ে গুরুত্বপূর্ণ অংশ।
| Dependency Array | কখন effect চলে |
|---|---|
| নেই (দেওয়াই হয়নি) | প্রতিবার render হওয়ার পর — প্রায়ই অনিচ্ছাকৃত, সতর্ক থাকতে হয় |
[] (খালি অ্যারে) |
শুধু একবার — component প্রথম রেন্ডার হওয়ার (mount) সময় |
[count] |
প্রথমবার, আর যতবার count-এর মান পাল্টাবে |
// প্রতিবার render হলেই চলবে (সতর্কতার সাথে ব্যবহার করা উচিত)
useEffect(() => {
console.log("প্রতিবার render হওয়ার পর");
});
// শুধু একবার — component প্রথম স্ক্রিনে আসার সময়
useEffect(() => {
console.log("component mount হয়েছে");
}, []);
// count পাল্টালেই আবার চলবে
useEffect(() => {
console.log("count এখন:", count);
}, [count]);
সবচেয়ে সাধারণ ব্যবহার — Mount-এ একবার চালানো
একটা খুব সাধারণ প্যাটার্ন হলো — component প্রথমবার স্ক্রিনে আসার সময় কিছু একটা সেটআপ করা, যেমন document title পাল্টানো।
function BookPage({ title }) {
useEffect(() => {
document.title = title + " — ArifStack"; // ব্রাউজার ট্যাবের শিরোনাম পাল্টানো
}, []); // শুধু একবার
return <h1>{title}</h1>;
}
লাইভ ডেমো — Dependency Array-এর প্রভাব
নিচে দুইটা কাউন্টার আছে — একটার effect শুধু একবার চলে (empty array), আরেকটার effect প্রতিবার count পাল্টালেই চলে। লগ দেখো কে কখন ট্রিগার হচ্ছে।
Counter A বাড়ালে B-এর effect চলে না, কিন্তু Counter B বাড়ালে শুধু B-এর effect আবার চলে — এটাই dependency array-এর কাজ।
Cleanup Function — জিনিসপত্র পরিষ্কার করা
কিছু সাইড ইফেক্টের একটা "cleanup" দরকার হয় — যেমন একটা timer বন্ধ করা, বা event listener সরিয়ে ফেলা, যাতে component চলে গেলে (unmount হলে) মেমরি লিক না হয়। effect ফাংশন থেকে আরেকটা ফাংশন রিটার্ন করে এটা করা যায়।
function WindowWidthTracker() {
const [width, setWidth] = React.useState(window.innerWidth);
useEffect(() => {
function handleResize() {
setWidth(window.innerWidth);
}
window.addEventListener("resize", handleResize);
// এই রিটার্ন করা ফাংশনটাই cleanup — component চলে গেলে React এটা কল করে
return () => {
window.removeEventListener("resize", handleResize);
};
}, []); // শুধু একবার listener বসানো, cleanup component চলে যাওয়ার সময়
return <p>উইন্ডোর প্রস্থ: {width}px</p>;
}
কেন cleanup জরুরি? যদি removeEventListener না করা হয়, component স্ক্রিন থেকে চলে গেলেও listener মেমরিতে "ঝুলে" থাকবে — এটাকে বলে memory leak। Advanced JS-এর Closure ধারণা (পর্ব ১) মনে করো — event listener-এর ভেতরের ফাংশন বাইরের ভ্যারিয়েবল "ধরে রাখে", তাই সরিয়ে না দিলে সেগুলো কখনো গার্বেজ কালেক্ট হয় না।
Timer-এর সাথে Cleanup
আরেকটা খুব সাধারণ cleanup উদাহরণ — setInterval ব্যবহার করলে সেটা বন্ধ করতে ভোলা যাবে না।
function LiveClock() {
const [time, setTime] = React.useState(new Date());
useEffect(() => {
const intervalId = setInterval(() => {
setTime(new Date());
}, 1000);
return () => {
clearInterval(intervalId); // component চলে গেলে টাইমার বন্ধ করা
};
}, []);
return <p>বর্তমান সময়: {time.toLocaleTimeString("bn-BD")}</p>;
}
লাইভ ডেমো — Cleanup সহ লাইভ ঘড়ি
প্রতি সেকেন্ডে setInterval দিয়ে সময় আপডেট হচ্ছে — cleanup ফাংশন নিশ্চিত করছে পেজ থেকে সরে গেলে টাইমার বন্ধ হয়ে যায়।
সাধারণ ভুল — অসীম লুপ
effect-এর ভেতরে যদি এমন কোনো state পাল্টানো হয়, যেটা আবার সেই একই effect-কে ট্রিগার করে, তাহলে অসীম লুপ তৈরি হতে পারে।
// ❌ অসীম লুপ! dependency array-ই নেই, প্রতি রেন্ডারে চলছে,
// আর প্রতিবার setCount কল হচ্ছে যেটা আবার render ট্রিগার করছে
function BuggyCounter() {
const [count, setCount] = React.useState(0);
useEffect(() => {
setCount(count + 1); // এটা re-render ঘটাবে, যা আবার effect চালাবে...
}); // dependency array নেই!
return <p>{count}</p>;
}
সমাধান: সবসময় ভাবো — এই effect-টা আসলে কখন চলা উচিত? সাধারণত [] (শুধু একবার) অথবা নির্দিষ্ট কিছু ভ্যারিয়েবল দেওয়াই নিরাপদ, dependency array পুরোপুরি বাদ দেওয়া কমই দরকার হয়।
নিজে চেষ্টা করো
Exercise:
- একটা component বানাও যেটা mount হওয়ার সময়
document.titleপাল্টায়,[]dependency দিয়ে। - উপরের
LiveClockনিজে বানাও, তারপর ইচ্ছাকৃতভাবেreturn(cleanup) লাইনটা মুছে console-এ দেখো কী warning আসে। WindowWidthTrackercomponent বানাও, ব্রাউজার সরু-চওড়া করে টেস্ট করো।- ইচ্ছাকৃতভাবে একটা অসীম লুপ বাগ তৈরি করো (উপরের
BuggyCounter-এর মতো), তারপর[]যোগ করে ঠিক করো।
পরের পর্বে যা থাকছে
পরের পর্বে আমরা শিখব useEffect দিয়ে Fetch API কল করা — কীভাবে একটা external API থেকে ডেটা আনা যায়, লোডিং স্টেট দেখানো যায়, আর ফলাফল component-এ দেখানো যায়।