অধ্যায়: ইভেন্ট আর ইফেক্ট—দুটো জিনিস, দুটো কাজ

শুরুতে একটু বুঝে নিই

React-এ কোড লেখার সময় অনেক সময় একটা প্রশ্ন উঠে — এই কোডটা আমি কোথায় রাখব? ইভেন্ট হ্যান্ডলারে, না ইফেক্টে?

এই প্রশ্নের উত্তর জানলে আপনি ঠিক সময়ে ঠিক কাজটা করতে পারবেন।

আমরা জানি — 👉 ইভেন্ট হ্যান্ডলার চলে যখন ইউজার কিছু করে (যেমন: ক্লিক করে)। 👉 আর ইফেক্ট চলে যখন কোনও ভ্যালু বদলায় (যেমন: স্টেট বা প্রপ বদলায়)।

এখন চিন্তা করুন, আপনি চ্যাট অ্যাপ বানাচ্ছেন।

আপনার টাস্ক দুটো:

  1. ইউজার যখন রুম সিলেক্ট করে, তখন সঙ্গে সঙ্গে সেই চ্যাট রুমে কানেক্ট হবে।
  2. যখন ইউজার “Send” বাটনে ক্লিক করবে, তখন সেই মেসেজ পাঠিয়ে দিতে হবে।

এই দুইটা কাজ কিন্তু একরকম না।

চলুন, ধাপে ধাপে দেখি কোন কাজটা কোথায় রাখা উচিত।


১. ইউজার ক্লিক করল, তাই কাজটা চলল — এটা ইভেন্ট হ্যান্ডলারের জন্য

মেসেজ পাঠানোর সময় চিন্তা করুন— ইউজার “Send” না চাপলে কি মেসেজ পাঠানো ঠিক হবে?

না, একদমই না। তখন ইউজার ভাববে, “আমি তো কিছু করিনি, মেসেজ গেল কীভাবে?”

তাই এই ধরনের কাজ ইভেন্ট হ্যান্ডলারের জন্য পারফেক্ট।

👉 ইভেন্ট হ্যান্ডলার মানে: ইউজার কিছু করলে তখনই সেই কাজটা হবে।

কোডটা কেমন হয় দেখুন:

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState("");
 
  function handleSendClick() {
    sendMessage(message);
  }
 
  return (
    <>
      <input value={message} onChange={(e) => setMessage(e.target.value)} />
      <button onClick={handleSendClick}>Send</button>
    </>
  );
}

এখানে sendMessage() তখনই চলবে, যখন ইউজার নিজে ক্লিক করবে। এই নিয়ন্ত্রণটা ইউজারের হাতে থাকে। এটাই ইভেন্ট হ্যান্ডলারের আসল উদ্দেশ্য।


১.২: যখন সিঙ্ক্রোনাইজ করা দরকার, তখন ইফেক্ট ব্যবহার করো

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

এটা কি ইউজার ক্লিক করল বলেই হবে?

না।

এই কোডটা চালানো দরকার কারণ, এখন ইউজার চ্যাট স্ক্রিনে আছে। সে মেসেজ পাঠাবে কি না, সেটা পরে দেখা যাবে — এখন তার সঙ্গে সার্ভারের কানেকশন থাকা দরকার।

তাই এই কাজটা হবে Effect দিয়ে।

ইফেক্ট তখন চলে, যখন ভ্যালু বদলায় — যেমন roomId

useEffect(() => {
  const connection = createConnection(serverUrl, roomId);
  connection.connect();
  return () => connection.disconnect();
}, [roomId]);

এখানে roomId যখনই বদলাবে (মানে ইউজার নতুন রুম সিলেক্ট করবে), তখন Effect আবার চলবে। আর আগের রুম থেকে disconnect হয়ে নতুন রুমে connect হবে।

পুরো কোডটা একবার দেখে নিই:

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState("");
 
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);
 
  function handleSendClick() {
    sendMessage(message);
  }
 
  return (
    <>
      <h1>{roomId} রুমে স্বাগতম!</h1>
      <input value={message} onChange={(e) => setMessage(e.target.value)} />
      <button onClick={handleSendClick}>Send</button>
    </>
  );
}

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


একটা সরল উপসংহার:

  • ইভেন্ট হ্যান্ডলার চালাও যখন ইউজার কিছু করল (যেমন বাটন ক্লিক)।
  • ইফেক্ট চালাও যখন ইউজার কিছু না করলেও, আপনার কম্পোনেন্টকে সিঙ্ক থাকা দরকার।

চমৎকার! চলুন আমরা Section 2 ও Section 2.1 একদম সহজ, প্রাঞ্জল বাংলায় লিখে ফেলি যেন React শেখা আরও আনন্দদায়ক ও বোধগম্য হয়।


২.১: রিয়াক্টিভ মান এবং রিয়াক্টিভ যুক্তি

React-এ কিছু জিনিস ম্যানুয়ালি চলে (ইউজার ক্লিক করলে), আবার কিছু জিনিস অটোমেটিক চলে (যখন দরকার হয়)। উদাহরণ:

  • Event handler চলে ইউজার কোনো অ্যাকশন নিলে।
  • Effect চলে এমনিতেই, যখন দরকার হয় — যাতে কম্পোনেন্ট সবসময় সিঙ্ক থাকে।

এবার একটু গভীরভাবে বোঝার চেষ্টা করি।

কোন জিনিসগুলো "reactive value"?

React-এ যেসব মান (value) পরিবর্তনের সাথে সাথে component আবার render হয়, সেগুলোকে বলে reactive value

যেমন নিচের কোডে—

const serverUrl = "https://localhost:1234";
 
function ChatRoom({ roomId }) {
  const [message, setMessage] = useState("");
}

এখানে—

  • serverUrl হলো সাধারণ ভেরিয়েবল। এটা reactive নয়।
  • কিন্তু roomId (prop) এবং message (state) হলো reactive values

কারণ, এই দুটো মান বদলালে component আবার render হয়।


ইভেন্ট হ্যান্ডলারের ভিতরের যুক্তি রিয়াক্টিভ নয়

ইভেন্ট হ্যান্ডলার যেমন:

function handleSendClick() {
  sendMessage(message);
}

এখানে message একটা reactive value হলেও, হ্যান্ডলারের ভিতরের কোড reactive নয়। মানে: message বদলালেই sendMessage(message) অটোমেটিক চলবে না।

কারণ?

আপনি কেবল লিখছেন মানে এই না যে আপনি মেসেজ পাঠাতে চাচ্ছেন।

আপনি Send বাটনে ক্লিক করলে তবেই sendMessage() চলবে — এটাই সঠিক আচরণ।


Effect-এর ভিতরের যুক্তি রিয়াক্টিভ

এখন যদি আমরা roomId এর মান পরিবর্তন হলে সার্ভারে কানেক্ট করতে চাই, তাহলে সেটা Effect-এ রাখি:

useEffect(() => {
  const connection = createConnection(serverUrl, roomId);
  connection.connect();
  return () => connection.disconnect();
}, [roomId]);

এখানে roomId বদলালেই Effect আবার চালু হবে। এটাই বলে: Effect হলো reactive, কারণ এটি reactive value (যেমন roomId) এর পরিবর্তনের উপর নির্ভর করে।


ছোট্ট তুলনা টেবিল:

বিষয়রিয়াক্টিভ?কবে চলে?
ইভেন্ট হ্যান্ডলারনাইউজার কিছু করলেই
ইফেক্টহ্যাঁনির্ভরশীল মান বদলালেই

উপসংহার:

React-এ আপনার যদি এমন কোন কাজ থাকে যা অবস্থার উপর নির্ভর করে আবার চলতে হয় (যেমন কানেকশন), তাহলে সেটা ইফেক্টে রাখুন। আর ইউজার যেদিন/যখন কিছু করবে (যেমন ক্লিক), তখন চালাতে চাইলে সেটা রাখুন ইভেন্ট হ্যান্ডলারে।


চলুন Section 2.3 একদম সহজভাবে, বাংলা ভাষায় লিখে ফেলি যেন Md. Mojnu Miah ভাই এবং অন্যরাও এটি পড়ে একবারেই বুঝে ফেলতে পারেন।


২.৩: ইফেক্টের ভিতরের যুক্তি রিয়াক্টিভ হয়

চলুন আবার নিচের কোডের দিকে ফিরে যাই:

const connection = createConnection(serverUrl, roomId);
connection.connect();

এখন প্রশ্ন হলো: এই কোডগুলো কি রিয়াক্টিভ হওয়া উচিত?

হ্যাঁ, হওয়া উচিত। কেন?

কারণ ইউজার যখন নতুন roomId বেছে নেন, তার মানে সে চায় নতুন রুমে কানেক্ট হতে। তাই এই কানেকশনের কোডটা অটোমেটিকভাবে আবার চলা দরকার — যতবার roomId বদলায়।

এ কারণেই আমরা এই কোডটা ইফেক্টের ভিতরে রাখি:

useEffect(() => {
  const connection = createConnection(serverUrl, roomId);
  connection.connect();
  return () => {
    connection.disconnect();
  };
}, [roomId]);

এখানে কী হচ্ছে?

  • যখন roomId বদলাবে, তখন এই ইফেক্ট আবার চলবে।
  • আগের কানেকশন ডিসকানেক্ট হয়ে যাবে।
  • নতুন রুমে কানেক্ট হবে।

এভাবেই ইফেক্ট reactive value এর সঙ্গে "সিঙ্ক" থাকে। React নিজেই এই আপডেটটা হ্যান্ডেল করে।


সংক্ষেপে বলা যায়:

কোড কোথায়রিয়াক্টিভ?কখন চলে
useEffect✅ হ্যাঁনির্ভরশীল মান (যেমন roomId) বদলালেই
handleClick (event handler)❌ নাশুধু ক্লিক করলে

বাস্তব জীবন উদাহরণ:

ধরুন আপনি একটা লাইভ চ্যাট অ্যাপে আছেন। আপনি Room-1 থেকে Room-2 তে গেলেন।

  • যদি ইফেক্ট না থাকত, তাহলে আপনি Room-2 সিলেক্ট করেও Room-1 এই থাকতেন!

  • কিন্তু ইফেক্ট থাকার কারণে React বুঝে নেয়:

    "আচ্ছা, roomId তো বদলেছে, তাহলে নতুন কানেকশন দরকার।"


চলুন এখন Section 3 সহজ ও স্বাভাবিক বাংলায় লিখে ফেলি যাতে তুমি এবং বাংলাদেশের যে কেউ সহজেই বুঝতে পারে।


৩. রিয়াক্টিভ ইফেক্ট থেকে নন-রিয়াক্টিভ যুক্তি আলাদা করা

React এর ইফেক্টে আমরা সাধারণত reactive logic রাখি — যেমন roomId বদলালে কানেকশন চেঞ্জ করা। কিন্তু অনেক সময় আমাদের প্রয়োজন পড়ে non-reactive logic রাখার, যেমন:

ইউজার চ্যাটে কানেক্ট হলে একটা notification দেখানো, যার কালার হবে থিম অনুযায়ী।

🧪 উদাহরণ দেখি:

useEffect(() => {
  const connection = createConnection(serverUrl, roomId);
  connection.on("connected", () => {
    showNotification("Connected!", theme);
  });
  connection.connect();
  return () => {
    connection.disconnect();
  };
}, [roomId, theme]); // ✅

এখানে theme একটা reactive মান, কারণ এটি ইউজার ইন্টার‌্যাকশনের ফলে পরিবর্তন হতে পারে। তাই theme কে dependency লিস্টে রাখা দরকার — না রাখলে React ওয়ার্নিং দিবে।


🎯 সমস্যা কোথায়?

theme বদলালেই ইফেক্ট আবার চলে — এবং পুরো চ্যাট কানেকশন আবার তৈরি হয়!

➡️ শুধু theme বদলানোর কারণে createConnection এবং connect() আবার চলা একেবারেই অপ্রয়োজনীয়!

আমরা চাচ্ছি শুধু notification-এর কালার বদলাতে — কিন্তু চ্যাট সার্ভারে আবার কানেক্ট করা হচ্ছে।

এটাই সমস্যা।


🔥 মূল পয়েন্ট:

নিচের এই লাইনটা reactive না হলেও useEffect এর ভিতর থাকার কারণে অপ্রয়োজনীয়ভাবে বারবার চলে:

showNotification("Connected!", theme);

🛠️ সমাধান কী?

আমাদের প্রয়োজন useEffect এর ভিতর থেকে theme-সম্পর্কিত notification-logic আলাদা করে ফেলা — যাতে theme বদলালেও চ্যাট কানেকশন ভাঙা না পড়ে।

এবং এর জন্য আমরা ব্যবহার করব:

  • নতুন useEffect শুধুমাত্র notification এর জন্য
  • অথবা useRef + callback pattern

এই অংশটা আমরা ব্যাখ্যা করব পরের সেকশনে।


🧠 সংক্ষেপে মনে রাখো:

কি রাখতে হবেকোথায় রাখতে হবেকখন চলবে
createConnection()রিয়াক্টিভ ইফেক্টযখন roomId বদলায়
showNotification()আলাদা ইফেক্ট বা callbackযখন চ্যাট কানেক্ট হয়

পরবর্তী সেকশন ৩.১-এ আমরা দেখব কীভাবে ইফেক্টের ভিতর থাকা এই "non-reactive logic" কে আলাদা করে রাখা যায় — যাতে ইউজারের একশনে অপ্রয়োজনীয় জিনিসগুলো আবার না চলে।

চমৎকার! এখন আমরা Section 3.1 এর জন্য বাংলা ভাষায় সহজ ও শিক্ষণীয়ভাবে লেখাটি সাজিয়ে নিই যেন React শেখার সময় নতুনরা বিষয়টা ভালোভাবে বুঝতে পারে।


Section 3.1: ইফেক্ট ইভেন্ট ডিক্লেয়ার করা

⚠️ এই সেকশনে যেই API ব্যবহার করা হয়েছে সেটি React-এর experimental API, অর্থাৎ এটি এখনও স্থায়ীভাবে রিলিজ হয়নি।

React-এ অনেক সময় useEffect হুকের মধ্যে এমন কিছু কোড রাখতে হয় যেটা React-এর রিঅ্যাক্টিভিটির (reactivity) অংশ নয় — অর্থাৎ, সেটা props বা state পরিবর্তনের কারণে বারবার চলা উচিত না।

এই ধরনের "non-reactive" কাজকে ইফেক্ট থেকে আলাদা করতে useEffectEvent নামের একটি বিশেষ হুক ব্যবহার করা হয়।

🎯 উদাহরণ দেখি:

import { useEffect, useEffectEvent } from "react";
 
function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification("Connected!", theme);
  });
  // ...
}

এখানে onConnected হচ্ছে একটি Effect Event। এটি useEffect হুকের বাইরে থাকে, কিন্তু আপনি এটিকে ইফেক্টের ভিতরে কল করতে পারেন। এর ভিতরের কোড রিঅ্যাক্টিভ না — মানে এটি বারবার চলবে না এবং সবসময় সর্বশেষ theme বা props/state এর মান দেখতে পাবে।


✅ এখন এটি ইফেক্টের ভিতরে কল করি:

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification("Connected!", theme);
  });
 
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on("connected", () => {
      onConnected();
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // ⚠️ এখানে onConnected ডিপেন্ডেন্সিতে দেয়া লাগবে না
}

মনে রাখুন: useEffectEvent দিয়ে তৈরি ইফেক্ট ইভেন্টগুলো reactive dependency list-এ রাখা হয় না।


অবশ্যই, নিচে Section 3.1 এ যেসব ফিচার চাওয়া হয়েছে বা যেভাবে কাজ করবে তা সহজ ভাষায় সিরিয়াল নম্বর দিয়ে লিখে দিলাম:


✅ Section 3.1-এ চাওয়া ফিচারগুলোর তালিকা:

১। নতুন সার্ভারে কানেক্ট হলে একটি নটিফিকেশন দেখাবে,   🔸 নোটিফিকেশন টেক্সট হবে: Connected!   🔸 থিম যদি ডার্ক হয়, তাহলে নোটিফিকেশন কালো ব্যাকগ্রাউন্ডে সাদা লেখায় দেখাবে,   🔸 থিম যদি লাইট হয়, তাহলে সাদা ব্যাকগ্রাউন্ডে কালো লেখায় দেখাবে।

২। নতুন কোনো রুমে ঢুকলে (roomId পরিবর্তন হলে)   🔸 সার্ভারের সাথে নতুন করে কানেকশন তৈরি হবে।   🔸 কানেকশন সফল হলে আবার নোটিফিকেশন দেখাবে।

৩। নোটিফিকেশন দেখানোর জন্য showNotification() ফাংশন ব্যবহার করা হবে।   🔸 এটি Toastify-js দিয়ে তৈরি করা।

৪। theme এর বর্তমান মান সবসময় ঠিকভাবে ব্যবহার হবে,   🔸 কারণ নোটিফিকেশন ফাংশনটি useEffectEvent দিয়ে তৈরি, তাই এটা প্রতিবার আপডেটেড theme পায়।

৫। নোটিফিকেশন দেখানো ফাংশনটি useEffect এর ভিতরে না রেখে আলাদা করা হয়েছে   🔸 কারণ এতে করে useEffect এর dependency list কমে গেছে এবং কোড পরিষ্কার হয়েছে।   🔸 এই আলাদা ফাংশনকে বলা হয় Effect Event

৬। Effect Event কে useEffect এর dependency list-এ দেওয়া লাগবে না,   🔸 কারণ এটি রিঅ্যাক্টিভ না — বরং ইভেন্ট হ্যান্ডলারের মতো ব্যবহার করা হয়।

৭। একটা ড্রপডাউন থেকে চ্যাট রুম সিলেক্ট করা যাবে (e.g., general, travel, music)।   🔸 রুম চেঞ্জ করলেই কানেকশন রি-ইনিশিয়েট হবে এবং নোটিফিকেশন দেখাবে।

৮। একটি চেকবক্স দিয়ে থিম চেঞ্জ করা যাবে (ডার্ক/লাইট)   🔸 চেঞ্জ করার সাথে সাথেই পরবর্তী নোটিফিকেশনে থিম পরিবর্তনের প্রভাব দেখা যাবে।


🎬 পূর্ণ উদাহরণ (লাইভ ডেমো):

এই কোডটিতে ChatRoom কম্পোনেন্ট একটি সার্ভারে কানেক্ট করে, কানেকশন সফল হলে showNotification ফাংশনের মাধ্যমে একটি নোটিফিকেশন দেখায়।

📦 প্যাকেজ ইনফো:

{
  "react": "experimental",
  "toastify-js": "1.12.0"
}

🧠 মূল অংশ:

import { useState, useEffect } from "react";
import { experimental_useEffectEvent as useEffectEvent } from "react";
import { createConnection } from "./chat.js";
import { showNotification } from "./notifications.js";
 
const serverUrl = "https://localhost:1234";
 
function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification("Connected!", theme);
  });
 
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on("connected", () => {
      onConnected();
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);
 
  return <h1>Welcome to the {roomId} room!</h1>;
}

👥 App কম্পোনেন্ট যেখানে রুম এবং থিম নির্বাচন করা যায়:

export default function App() {
  const [roomId, setRoomId] = useState("general");
  const [isDark, setIsDark] = useState(false);
 
  return (
    <>
      <label>
        Choose the chat room:{" "}
        <select value={roomId} onChange={(e) => setRoomId(e.target.value)}>
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <label>
        <input
          type="checkbox"
          checked={isDark}
          onChange={(e) => setIsDark(e.target.checked)}
        />
        Use dark theme
      </label>
      <hr />
      <ChatRoom roomId={roomId} theme={isDark ? "dark" : "light"} />
    </>
  );
}

📦 showNotification ফাংশন:

import Toastify from "toastify-js";
import "toastify-js/src/toastify.css";
 
export function showNotification(message, theme) {
  Toastify({
    text: message,
    duration: 2000,
    gravity: "top",
    position: "right",
    style: {
      background: theme === "dark" ? "black" : "white",
      color: theme === "dark" ? "white" : "black",
    },
  }).showToast();
}

🧠 সংক্ষেপে বুঝি:

বিষয়ব্যাখ্যা
useEffectEventReact-এর experimental Hook, non-reactive কোডের জন্য ব্যবহৃত
ইফেক্ট ইভেন্টevent handler এর মত আচরণ করে, কিন্তু আপনি নিজেই কল করেন
dependency listএতে onConnected এর মত ইফেক্ট ইভেন্ট যোগ করা যাবে না

পরবর্তী ধাপে: আমরা দেখবো কীভাবে এই useEffectEvent আমাদের আরও জটিল ইফেক্ট গুলোকে ক্লিন এবং বাগ-মুক্ত রাখতে সাহায্য করে।


চমৎকার! এখন আমরা তোমার React বইয়ের Section 3.2: Effect Event দিয়ে Props এবং State পড়া অংশটি বাংলায় লিখে নিচ্ছি, যাতে এটা ডকুমেন্টেশনের মতো পরিষ্কার, প্রাঞ্জল এবং প্র্যাক্টিকাল উদাহরণে সমৃদ্ধ হয়।


সেকশন ৩.২: ইফেক্ট ইভেন্ট দিয়ে Props ও State-এর সর্বশেষ মান পড়া

⚠️ এই সেকশনে যা শিখবেন, তা React-এর একটি Experimental (পরীক্ষামূলক) API নিয়ে, যেটি এখনো স্থায়ীভাবে রিলিজ হয়নি।

React-এর নতুন useEffectEvent হুক আপনাকে এমন অনেক সমস্যার সমাধান করতে সাহায্য করবে যেখানে আপনি হয়তো useEffect-এর ডিপেন্ডেন্সি লিন্টারকে চেপে যেতে চাইতেন।

সমস্যা শুরু হয় যেভাবে

ধরুন, আপনি useEffect ব্যবহার করে পেজ ভিজিট লগ করছেন:

function Page() {
  useEffect(() => {
    logVisit();
  }, []);
}

পরে আপনি রাউটিং যুক্ত করলেন, এবং Page কম্পোনেন্ট এখন url নামের একটি প্রপ্স পাচ্ছে। আপনি url লগ করতে চান:

function Page({ url }) {
  useEffect(() => {
    logVisit(url);
  }, []); // 🔴 মিসিং ডিপেন্ডেন্সি: url
}

এখানে url ছাড়া দিলে React লিন্টার সতর্ক করবে। কেন? কারণ আপনি url ব্যবহার করছেন, কিন্তু useEffect-এর ডিপেন্ডেন্সি লিস্টে দেননি।

সঠিক সমাধান হবে:

function Page({ url }) {
  useEffect(() => {
    logVisit(url);
  }, [url]); // ✅ ডিপেন্ডেন্সি ঠিকঠাক
}

আরেকটু জটিল বাস্তব উদাহরণ

এবার আপনি চান logVisit-এর সঙ্গে শপিং কার্টে কয়টি আইটেম আছে সেটাও পাঠাতে:

function Page({ url }) {
  const { items } = useContext(ShoppingCartContext);
  const numberOfItems = items.length;
 
  useEffect(() => {
    logVisit(url, numberOfItems);
  }, [url]); // 🔴 লিন্টার বলবে: numberOfItems মিসিং
}

কিন্তু আপনি চান না numberOfItems বদলালে logVisit আবার কল হোক। কারণ, ইউজার যদি কার্টে কিছু যোগ করে, এটা নতুন ভিজিট নয়।

সমাধান: useEffectEvent

এই সমস্যা সমাধানের জন্য আমরা useEffectEvent ব্যবহার করব:

function Page({ url }) {
  const { items } = useContext(ShoppingCartContext);
  const numberOfItems = items.length;
 
  const onVisit = useEffectEvent((visitedUrl) => {
    logVisit(visitedUrl, numberOfItems);
  });
 
  useEffect(() => {
    onVisit(url);
  }, [url]); // ✅ শুধুমাত্র url বদলালে লগ হবে
}

এখানে যা ঘটছে:

  • onVisit হলো একটি ইফেক্ট ইভেন্ট।
  • এর ভেতরে আপনি numberOfItems ব্যবহার করছেন, কিন্তু এটা reactive নয়, মানে এর বদল ইফেক্ট রিরান করাবে না।
  • বাইরের useEffect শুধু url-এর উপর রিএক্টিভ, তাই প্রতিবার নতুন পেজে এলে onVisit(url) কল হবে।

✅ কেন url প্যারামিটার দিয়ে পাঠানো ভালো?

আপনি চাইলে onVisit() এর ভিতর থেকেই url পড়তে পারতেন:

const onVisit = useEffectEvent(() => {
  logVisit(url, numberOfItems);
});
 
useEffect(() => {
  onVisit();
}, [url]);

কিন্তু এটা স্পষ্ট নয় যে কোন url লগ হচ্ছে। তাই এটা আরও পরিষ্কারভাবে লেখাই ভালো:

const onVisit = useEffectEvent((visitedUrl) => {
  logVisit(visitedUrl, numberOfItems);
});
 
useEffect(() => {
  onVisit(url);
}, [url]);

এতে url ইভেন্টের একটি অংশ হিসেবে আলাদা করে পাঠানো হয়, এবং আপনি ভুল করে url ডিপেন্ডেন্সি বাদ দিলে লিন্টার সেটা ধরবে।

🕒 অ্যাসিনক্রোনাস (দেরিতে ঘটে এমন) লজিকেও কাজে লাগে

useEffect(() => {
  setTimeout(() => {
    onVisit(url);
  }, 5000); // ৫ সেকেন্ড পরে লগ
}, [url]);

এখানে url বদলালেও onVisit-এর মধ্যে visitedUrl মানে সেই সময়কার ঠিক url-টা ধরবে। এটা useEffectEvent এর শক্তি।


🚫 ডিপেন্ডেন্সি লিন্টার সাপ্রেস (চেপে) করা উচিত নয়

অনেক কোডবেসে দেখা যায়, এরকম লেখা হয়:

useEffect(() => {
  logVisit(url, numberOfItems);
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, [url]);

এতে সমস্যার শুরু হয়। যেমন ধরুন নিচের কোডে:

function handleMove(e) {
  if (canMove) {
    setPosition({ x: e.clientX, y: e.clientY });
  }
}
 
useEffect(() => {
  window.addEventListener("pointermove", handleMove);
  return () => window.removeEventListener("pointermove", handleMove);
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

এখানে canMove বদলালেও handleMove পুরোনো মানে আটকে যায়। কেন? কারণ handleMove কে রিএক্টিভ মান হিসাবে [handleMove] ডিপেন্ডেন্সিতে না দিলে React সেটিকে আপডেট করে না।

✅ সঠিক সমাধান: useEffectEvent দিয়ে

const onMove = useEffectEvent((e) => {
  if (canMove) {
    setPosition({ x: e.clientX, y: e.clientY });
  }
});
 
useEffect(() => {
  window.addEventListener("pointermove", onMove);
  return () => window.removeEventListener("pointermove", onMove);
}, []);

এখানে onMove সবসময় canMove-এর সর্বশেষ মান ব্যবহার করবে।


উপসংহার

  • useEffectEvent এমন ক্ষেত্রে পারফেক্ট যেখানে আপনি ইফেক্টে কিছু “ইভেন্টের মতো” কাজ করতে চান।
  • এটা আপনাকে stale মান (পুরোনো স্টেট/প্রপস) নিয়ে চিন্তা করতে দেয় না।
  • আপনি ডিপেন্ডেন্সি লিন্টারকে সাপ্রেস না করে React-এর সেরা সুবিধা নিতে পারেন।

চমৎকার! এখন Section 3.3 এর বাংলা অনুবাদ এবং ব্যাখ্যা দেওয়া যাক React Bangla Tutorial বইয়ের স্টাইলে — সহজ ভাষায়, উদাহরণসহ, শিক্ষানবিশদের বোঝার উপযোগীভাবে।


🧠 অধ্যায় ৩.৩: Effect Event-এর সীমাবদ্ধতা

⚠️ এই অধ্যায়ে আমরা যে API নিয়ে কথা বলছি, তা এখনো React-এর স্থায়ী (Stable) ভার্সনে আসেনি। এটি একটি পরীক্ষামূলক (Experimental) API।

useEffectEvent() দেখতে যতটা সহজ, ব্যবহার করতে গেলে কিছু সীমাবদ্ধতা মাথায় রাখতে হবে।


🚫 Effect Event কখন ব্যবহার করা যাবে না?

Effect Event কখনোই নিচের কাজগুলোতে ব্যবহার করা যাবে না:

  1. 🔁 Effect ছাড়া অন্য জায়গা থেকে এটি কল করা যাবে না।
  2. 🚷 Effect Event অন্য কোন কম্পোনেন্টে বা কাস্টম হুকে পাস করা যাবে না।

❌ ভুল উদাহরণ:

function Timer() {
  const [count, setCount] = useState(0);
 
  const onTick = useEffectEvent(() => {
    setCount(count + 1);
  });
 
  useTimer(onTick, 1000); // 🔴 এভাবে করা যাবে না!
 
  return <h1>{count}</h1>;
}
 
function useTimer(callback, delay) {
  useEffect(() => {
    const id = setInterval(() => {
      callback(); // এখানে Effect Event ডাকা হয়েছে
    }, delay);
    return () => clearInterval(id);
  }, [delay, callback]);
}

এই কোডে onTick একটি Effect Event, কিন্তু আমরা তা একটা কাস্টম হুক useTimer-এ পাঠিয়েছি। যা React-এর নিয়ম অনুযায়ী সঠিক নয়।


✅ সঠিক পদ্ধতি:

Effect Event কে সবসময় সেই Effect-এর পাশে রাখো যেটা তা ব্যবহার করছে।

function Timer() {
  const [count, setCount] = useState(0);
  useTimer(() => {
    setCount(count + 1);
  }, 1000);
  return <h1>{count}</h1>;
}
 
function useTimer(callback, delay) {
  const onTick = useEffectEvent(() => {
    callback();
  });
 
  useEffect(() => {
    const id = setInterval(() => {
      onTick(); // ✅ Effect এর ভিতর থেকে লোকালভাবে কল করা হচ্ছে
    }, delay);
    return () => clearInterval(id);
  }, [delay]); // এখানে onTick কে dependency হিসেবে দিতে হয়নি!
}

এই কোডে onTick শুধু Effect-এর ভিতরেই ব্যবহৃত হয়েছে। এটা পুরোপুরি বৈধ এবং পারফরম্যান্সের দিক থেকে ভালো।


🔁 মনে রাখার বিষয় (Recap)

বিষয়ব্যাখ্যা
🖱️ ইভেন্ট হ্যান্ডলারব্যবহারকারীর action (click, input ইত্যাদি)-এর রেসপন্সে চলে
🔄 ইফেক্ট (Effect)যখন component-এর কোন state বা prop পরিবর্তন হয়, তখন চলে
❌ ইভেন্ট হ্যান্ডলারের ভিতরের লজিকreactive নয়
✅ ইফেক্টের ভিতরের লজিকreactive
🔀 Effect Eventইফেক্টের ভিতর থেকে call করার জন্য non-reactive কোড আলাদা রাখে
📍 Effect Event ব্যবহারশুধুমাত্র ইফেক্টের ভিতরে কল করতে হবে
🛑 Effect Event পাস করাঅন্য কম্পোনেন্ট বা হুকে পাস করা যাবে না

📌 সহজভাবে বললে: Effect Event হচ্ছে এমন একটি helper, যেটা শুধুমাত্র useEffect-এর ভিতরে কিছু non-reactive logic চালানোর জন্য। এটি কখনো বাইরের দিকে share বা reuse করা যাবে না।