ধন্যবাদ, Mojnu ভাই! তুমি একদম ঠিক ধরেছো—আরও একটু সহজ, সরল আর প্রাঞ্জলভাবে লেখা গেলে পাঠকের জন্য আরও বোধগম্য হবে। চল, আমি পুরোটা অংশ আরও সহজ করে বাংলায় রূপান্তর করে দিচ্ছি যেন React শেখা হয় মজা দিয়ে, চাপ দিয়ে নয়।


অধ্যায়: Effect এর অপ্রয়োজনীয় dependency বাদ দেওয়া

(Removing Effect Dependencies)

React-এ useEffect() ব্যবহার করলে, আমরা একটা কোড চালাতে পারি যখন component রেন্ডার হয় বা কোনো নির্দিষ্ট মান (যেমন props বা state) বদলায়।

এই কোডটা যেন ঠিক সময়ে চলে, সেটা ঠিক করার জন্য React বলে দেয়—তোমার effect কোন কোন মানের উপর নির্ভর করছে, সেই মানগুলো dependency list-এ দিতে হবে। মানে তুমি যদি useEffect(() => {}, [এখানে মান]) লেখো, তাহলে সেখানে যেসব মান পরিবর্তন হলে effect চলা উচিত—সেগুলো দিতে হবে।

যদি ভুল dependency দাও বা কোনোটা না দাও, তাহলে effect:

  • ভুল সময়ে চলবে
  • অপ্রয়োজনীয়ভাবে বারবার চলতে পারে
  • এমনকি অসীম লুপে ঢুকে যেতে পারে!

এই অধ্যায়ে শিখব:

  • কিভাবে ভুল dependency দিলে ইনফিনিট লুপ হয়, আর কিভাবে ঠিক করতে হয়
  • কখন কোনো dependency বাদ দেওয়া যায়
  • কিভাবে এমন কিছু পড়া যায় যেটার পরিবর্তনে React যেন effect না চালায়
  • object বা function কে dependency বানালে কেন সমস্যা হয়
  • dependency লিন্টারকে বন্ধ (suppress) করা কেন খারাপ ধারণা

📘 অংশ ১: কোড যা পড়ে, dependency তাও হওয়া উচিত

(Dependencies should match the code)

চলো আমরা একটা সহজ উদাহরণ দেখি।

ধরো, আমরা একটা ChatRoom বানাচ্ছি যেখানে ইউজার একটা রুম নির্বাচন করতে পারে। আমরা চাই—প্রতি বার রুম বদলালে নতুন connection তৈরি হোক।

const serverUrl = 'https://localhost:1234';
 
function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  	// ...
}

এখানে আমরা roomId ব্যবহার করছি effect-এর ভিতরে। কিন্তু যদি dependency list খালি রাখি—

useEffect(() => {
  // ...
}, []); // ❌ ভুল

তাহলে React জানে না যে roomId বদলালে আবার effect চালানো দরকার। তখন effect চলবে না—এবং রুম পরিবর্তন হলেও আগের রুমেই connection থেকে যাবে। 😕

React এর লিন্টার আমাদের বলে দেবে: "Hey! তুমি roomId ব্যবহার করেছো, কিন্তু dependency list-এ দাওনি!"

তখন ঠিক করি এভাবে:

useEffect(() => {
  const connection = createConnection(serverUrl, roomId);
  connection.connect();
  return () => connection.disconnect();
}, [roomId]); // ✅ ঠিক করলাম

🧠 মনে রাখো: effect-এর ভিতরে যেসব মান (props বা state) তুমি পড়ছো, সেগুলো reactive মান। মানে, পরিবর্তন হলে React আবার রেন্ডার করে। এসব মানকে dependency-তে দিতে হয়, যেন React বুঝতে পারে কখন effect আবার চালাতে হবে।


✅ উদাহরণসহ সহজ ডেমো

তোমার বোঝার সুবিধার জন্য নিচে একটা পূর্ণাঙ্গ উদাহরণ দিলাম—

import { useState, useEffect } from "react";
import { createConnection } from "./chat.js";
 
const serverUrl = "https://localhost:1234";
 
function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // ✅ roomId কে dependency হিসাবে দিলাম
  return <h1>Welcome to the {roomId} room!</h1>;
}
 
export default function App() {
  const [roomId, setRoomId] = useState("general");
  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>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}
export function createConnection(serverUrl, roomId) {
  return {
    connect() {
      console.log(
        '✅ Connecting to "' + roomId + '" room at ' + serverUrl + "..."
      );
    },
    disconnect() {
      console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl);
    },
  };
}

🔍 সারাংশ

সমস্যাকারণসমাধান
effect রুম পরিবর্তনে চালাচ্ছে নাdependency list-এ roomId নাই[roomId] দাও
effect বারবার অপ্রয়োজনীয়ভাবে চলছেঅপ্রয়োজনীয় মান দিয়েছো dependency-তেশুধু দরকারি মান দাও
ইনফিনিট লুপ হচ্ছেobject/function বারবার নতুন হচ্ছেmemo বা callback ইউজ করো (পরে শিখবো)

ধন্যবাদ Mojnu ভাই! চলো তাহলে আমরা Section 1.1: “একটা dependency বাদ দিতে চাইলে, প্রমাণ করতে হবে ওটা আসলেই dependency না”—এই অংশটা তোমার ভাষা আর শিক্ষার্থীদের বোঝার জন্য আরও সরল, সুন্দর আর প্রাঞ্জল বাংলায় লিখে ফেলি।


🧩 ১.১: কোনো dependency বাদ দিতে হলে, প্রমাণ করতে হবে ওটা সত্যিই dependency না

(To remove a dependency, prove that it's not a dependency)

React এ useEffect() এর ভেতরে যেসব reactive মান (যেমন props বা component-এর ভিতরের state বা ফাংশন) ব্যবহার করা হয়, সেগুলোকে তোমার dependency list-এ অবশ্যই দিতে হবে। এটা তোমার ইচ্ছেমতো না—React নিজেই নির্ধারণ করে নেয় effect-এর কোড কোন কোন মানের উপর নির্ভর করছে।

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

const serverUrl = "https://localhost:1234";
 
function ChatRoom({ roomId }) {
  // roomId হলো reactive মান
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId); // roomId এখানে effect-এর ভিতরে ব্যবহার হচ্ছে
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // ✅ তাই এটাকে dependency list-এ রাখতে হবে
}

👉 উপরের কোডে roomId একটি prop, মানে এটা React-এর দৃষ্টিতে reactive value। এটা যদি পরিবর্তিত হয়, তাহলে effect-এর নতুন করে চলা দরকার। তাই roomId-কে dependency list-এ না দিলে সমস্যা হবে।


❌ যদি dependency না দাও?

const serverUrl = "https://localhost:1234";
 
function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, []); // 🔴 ভুল: roomId বাদ দেওয়া হয়েছে
}

এখানে React লিন্টার সতর্ক করবে:

⚠️ "React Hook useEffect has a missing dependency: 'roomId'"

এই সতর্কতা সঠিক, কারণ:

  • roomId পরিবর্তিত হলে effect নতুন করে চলা দরকার
  • কিন্তু তুমি বলেছো: “না, কোনো dependency নাই”
  • ফলে আগের রুমেই connect হয়ে থাকবে, নতুন রুমে যাবে না—এটা একটা bug

✅ তাহলে dependency বাদ দিতে চাইলে কী করতে হবে?

তোমাকে React-কে বুঝিয়ে দিতে হবে যে এই মানটি আর reactive না, মানে এটা রেন্ডার হলে পরিবর্তিত হবে না।

উদাহরণ:

const serverUrl = "https://localhost:1234";
const roomId = "music"; // এটা এখন component-এর বাইরে, তাই reactive না
 
function ChatRoom() {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, []); // ✅ এখানে dependency দরকার নেই, কারণ roomId এখন অপরিবর্তনীয়
}

এখন roomId হচ্ছে একটা static মান—component-এর বাইরে ডিক্লেয়ার করা হয়েছে, কোনো props বা state না। কাজেই এটা কখনো পরিবর্তিত হবে না, effect আবার চালানোরও দরকার নেই।


🔗 Sandpack ডেমো (সম্পূর্ণ কোড)

import { useEffect } from "react";
import { createConnection } from "./chat.js";
 
const serverUrl = "https://localhost:1234";
const roomId = "music"; // এখন এটা static
 
export default function ChatRoom() {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, []); // ✅ কেবল একবার চলবে, কারণ কোনো reactive মানে নির্ভর করছে না
  return <h1>Welcome to the {roomId} room!</h1>;
}
export function createConnection(serverUrl, roomId) {
  return {
    connect() {
      console.log(
        '✅ Connecting to "' + roomId + '" room at ' + serverUrl + "..."
      );
    },
    disconnect() {
      console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl);
    },
  };
}

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

বিষয়ভালো ব্যবহারখারাপ ব্যবহার
reactive মান (যেমন props/state) effect-এ পড়লেdependency list-এ দিতে হবেবাদ দিলে bug হবে
static মান (component-এর বাইরে ডিক্লেয়ার) effect-এ পড়লেdependency list-এ দেওয়া লাগবে নাReact চিন্তিতও হবে না
dependency বাদ দিতে চাইলেপ্রমাণ করো যে মানটি আর reactive নাশুধু [] দিয়ে দিলে চলবে না

চলুন Mojnu ভাই, এবার আমরা Section 1.2 লিখি বাংলা ভাষায়, সহজ-সরলভাবে যেন আপনার পাঠকরা (React শেখার শুরুতে থাকা ডেভেলপাররা) ভালোভাবে বুঝতে পারে।


🧩 ১.২: Dependency বদলাতে হলে, কোড বদলাও

(To change the dependencies, change the code)

React-এ useEffect() এর dependency list আপনি ইচ্ছামতো ঠিক করতে পারবেন না। আপনার effect-এর কোড যেসব reactive মান ব্যবহার করছে, সেই অনুযায়ী dependency list অটোমেটিক নির্ধারণ হয়।

🧠 কীভাবে কাজ করে?

React-এ কাজ করার সময় এই তিনটি ধাপ প্রায়ই ঘটে:

  1. আপনি প্রথমে effect-এর ভিতরের কোড বা মান (props/state) পরিবর্তন করেন।
  2. এরপর React লিন্টার আপনাকে বলে কোন মানগুলো dependency list-এ থাকতে হবে।
  3. আপনি যদি এই dependency list পছন্দ না করেন, তাহলে আবার effect-এর কোডই পরিবর্তন করতে হয়।

সুতরাং, dependency list বদলাতে চাইলে, effect-এর কোডটাই আগে বদলাতে হবে।


🧮 এটা অনেকটা অঙ্ক মেলানোর মতো

আপনি ধরুন একটা dependency কমাতে চাচ্ছেন। তখন আপনাকে খুঁজে বের করতে হবে এমন কোড, যেটা সেই dependency-র উপর নির্ভর না করে চলে। যেমন অঙ্কে সমীকরণ মিলিয়ে একটা মান বের করতে হয়—ঠিক তেমন।


⚠️ সাবধান: লিন্টার সাপ্রেস করা মানে React-কে ভুল তথ্য দেওয়া

অনেক পুরোনো কোডবেসে এইরকম লাইন দেখা যায়:

useEffect(() => {
  // ...
  // 🔴 ভুল: লিন্টার সাইলেন্ট করে ফেলা
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

এভাবে লিন্টারকে থামিয়ে দেওয়ার মানে React-কে মিথ্যা বলা—effect যে মানের উপর নির্ভর করছে, সেটা React-কে জানানো হচ্ছে না। ফলে, React ভুলভাবে আচরণ করে এবং খুব বাজে ধরনের বাগ তৈরি হয়, যেগুলো খুঁজে বের করাও কঠিন।


💥 বাস্তব উদাহরণ: যখন effect সঠিকভাবে আপডেট হয় না

import { useState, useEffect } from "react";
 
export default function Timer() {
  const [count, setCount] = useState(0);
  const [increment, setIncrement] = useState(1);
 
  function onTick() {
    setCount(count + increment);
  }
 
  useEffect(() => {
    const id = setInterval(onTick, 1000);
    return () => clearInterval(id);
    // eslint-disable-next-line react-hooks/exhaustive-deps ❌
  }, []);
 
  return (
    <>
      <h1>
        Counter: {count}
        <button onClick={() => setCount(0)}>Reset</button>
      </h1>
      <hr />
      <p>
        প্রতি সেকেন্ডে ইনক্রিমেন্ট হবে:
        <button
          disabled={increment === 0}
          onClick={() => setIncrement((i) => i - 1)}
        >

        </button>
        <b>{increment}</b>
        <button onClick={() => setIncrement((i) => i + 1)}>+</button>
      </p>
    </>
  );
}

🤔 কী সমস্যা হলো?

  • আপনি increment মানটা বাড়ালেন, কিন্তু counter একই রকমই থেকে যাচ্ছে!
  • কারণ: React মনে করছে onTick কখনোই বদলাবে না। কারণ আপনি dependency list-এ কিছুই দেননি।
  • কিন্তু onTick এর ভিতরে আছে count আর increment — যেগুলো প্রতিবার রেন্ডারে নতুন হয়
  • তাই onTick পুরোনো মান (count = 0, increment = 1) ধরে রাখছে, এবং প্রতি সেকেন্ডে setCount(0 + 1) করেই চলেছে।

ফলে আপনার counter কখনোই ১-এর বেশি হবে না।


✅ সমাধান কী?

এই ধরণের বাগ থেকে বাঁচার জন্য dependency list ঠিকভাবে দিতে হবে।

যদি আপনি চান onTick এর ভিতরে সবসময় সর্বশেষ count আর increment ব্যবহার হোক, তাহলে onTick কে effect event বানাতে হবে। (যেটা আমরা পরের অধ্যায়ে বিস্তারিত আলোচনা করব)


🧠 মনে রাখার নিয়ম

ভুল কাজকেন ভুলসঠিক বিকল্প
useEffect এ dependency না দিয়ে eslint-ignore ব্যবহারReact ভুল ধারণা নেয় যে effect কারও উপর নির্ভর করছে নাকোড বদলাও, যাতে dependency কমে
পুরোনো count বা increment মানে সেট করাeffect কখনো আপডেট হচ্ছে নাeffect event বা updater function ব্যবহার
শুধু [] দিয়ে effect চালানোএটা বলে যে effect কেবল mount-এ চলবেযদি ভিতরে reactive মান থাকে, [dependency] দাও

📌 উপসংহার

React-এ useEffect() এর dependency list কখনোই ইচ্ছেমতো নিয়ন্ত্রণ করা যাবে না। সেটা effect-এর কোড দেখে তৈরি হয়। তাই dependency list পাল্টাতে চাইলে effect-এর ভিতরের reactive মানের ব্যবহার পাল্টাতে হবে।

❌ লিন্টারকে চুপ করানো কোনো সমাধান না।

✅ dependency মেলাতে না পারলে effect বা কোড স্ট্রাকচার বদলাও।


Md. Mojnu Miah, নিচে Section 2: Removing unnecessary dependencies এবং Subsection 2.1: Should this code move to an event handler? এর সহজ ভাষায় বাংলা ব্যাখ্যা দিলাম, যেন তুমি React শেখার সময় সহজে বুঝতে পারো:


সেকশন ২: অপ্রয়োজনীয় ডিপেনডেন্সি সরানো

যখনই তুমি useEffect এ ডিপেনডেন্সি যোগ করো, তখন একবার ভাবো— 👉 এই Effect-টা আসলেই কি ওই ডিপেনডেন্সি বদলালে চলা উচিত?

অনেক সময় উত্তর হয় না। যেমন:

  • তুমি চাও Effect-এর আলাদা অংশ যেন আলাদা শর্তে চলে।
  • তুমি চাও শুধু কোনো মান রিড করতে (পড়তে), রিএক্ট না করতে।
  • কোনো ডিপেনডেন্সি (যেমন ফাংশন বা অবজেক্ট) অনেক বেশি বার বদলায়—যা তুমি চাও না।

এই সমস্যাগুলোর সমাধান করতে হলে, তোমার Effect-এর কোডটা নিয়ে প্রশ্ন করতে হবে।


সেকশন ২.১: এই কোডটা কি ইভেন্ট হ্যান্ডলারে থাকা উচিত?

প্রথম প্রশ্নটাই হওয়া উচিত: 👉 "এই কোডটা কি সত্যিই Effect এ থাকা দরকার, নাকি ইভেন্ট হ্যান্ডলারে রাখা ভালো?"

ভুল উদাহরণ:

ধরো, তোমার একটা ফর্ম আছে। ফর্ম সাবমিট হলে submitted state true হয়। তখন তুমি POST রিকোয়েস্ট পাঠাও আর একটি নোটিফিকেশন দেখাও। তুমি এটা useEffect এর মধ্যে লিখলে:

useEffect(() => {
  if (submitted) {
    post("/api/register");
    showNotification("Successfully registered!");
  }
}, [submitted]);

পরে, তুমি theme ব্যবহার করতে চাইলে:

const theme = useContext(ThemeContext);
 
useEffect(() => {
  if (submitted) {
    post("/api/register");
    showNotification("Successfully registered!", theme);
  }
}, [submitted, theme]);

এখন ধরো তুমি ফর্ম সাবমিট করেছো, এরপর থিম চেঞ্জ করলে theme বদলে যাবে। তখন Effect আবার চলবে, এবং আগের নোটিফিকেশন আবার দেখাবে—যা তুমি চাও না।


✅ সমাধান:

এই ইভেন্টটা তো হয়েছে ফর্ম সাবমিট করার সময়। তাহলে POST রিকোয়েস্ট এবং নোটিফিকেশন ইভেন্ট হ্যান্ডলারে রাখা উচিত, Effect-এ নয়।

function handleSubmit() {
  post("/api/register");
  showNotification("Successfully registered!", theme);
}

এখন theme যত বদলাক না কেন, শুধু সাবমিট করার সময়ই কোডটা চলবে।


উপসংহার:

ইফেক্ট শুধু তখনই ব্যবহার করো, যখন React নিজে থেকে কোনো state বা prop-এর পরিবর্তনে কোড চালাতে হবে।

আর যদি কোনো কোড ব্যবহারকারীর কোনো অ্যাকশনের ফলে চালাতে চাও (যেমন ক্লিক, সাবমিট)— তাহলে সেটা ইভেন্ট হ্যান্ডলারে রাখো।

Md. Mojnu Miah, নিচে Section 2.2: Is your Effect doing several unrelated things? এর সহজ ভাষায় বাংলায় ব্যাখ্যা দিলাম, যেন তুমি স্পষ্টভাবে বুঝতে পারো কেন useEffect-এ একাধিক কাজ রাখা বিপদ ডেকে আনতে পারে:


সেকশন ২.২: আপনার Effect কি অনেকগুলো অপ্রাসঙ্গিক কাজ করছে?

প্রশ্ন করো: 👉 "এই useEffect কি একসাথে একাধিক আলাদা-ধরনের কাজ করছে?"

উদাহরণ:

ধরো, তোমার একটা ShippingForm আছে। ইউজার আগে দেশ (country) সিলেক্ট করবে, তারপর শহর (city), তারপর এলাকার (area)।

প্রথম ধাপ:

যখন ইউজার একটা দেশ সিলেক্ট করে, তখন সেই দেশের সব শহরের লিস্ট সার্ভার থেকে আনা দরকার।

✅ এই কাজ ঠিকভাবে করা হয়েছে:

useEffect(() => {
  fetch(`/api/cities?country=${country}`)
    .then(...); // cities ফেচ করে সেট করা হচ্ছে
}, [country]);

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


কিন্তু সমস্যা শুরু হয় যখন...

তুমি একই useEffect-এর ভিতরে city অনুযায়ী area ফেচ করার কাজও লিখে ফেলো:

useEffect(() => {
  // country অনুযায়ী cities ফেচ
  // city অনুযায়ী areas ফেচ
}, [country, city]);

এখন সমস্যা হলো:

  • ইউজার যদি শুধু city বদলায়, তাহলে useEffect আবার চলে যাবে।
  • কিন্তু তোমার কোডে country-ও আবার ফেচ হয়ে যাবে (যেটা দরকার ছিল না)। এইভাবে city পাল্টালেও আবার country-এর জন্য city লিস্ট ফেচ হবে—যা একদমই অপ্রয়োজনীয়।

✅ সমাধান: কাজগুলো আলাদা করো

যেহেতু:

  • country বদলালে শহরের লিস্ট ফেচ করতে হয়
  • city বদলালে এরিয়ার লিস্ট ফেচ করতে হয়

তাই দুটি আলাদা useEffect লিখো:

// Country অনুযায়ী Cities ফেচ করার Effect
useEffect(() => {
  fetch(`/api/cities?country=${country}`)
    .then(...); // setCities
}, [country]);
 
// City অনুযায়ী Areas ফেচ করার Effect
useEffect(() => {
  if (city) {
    fetch(`/api/areas?city=${city}`)
      .then(...); // setAreas
  }
}, [city]);

এখন:

  • country বদলালে শুধু cities ফেচ হবে।
  • city বদলালে শুধু areas ফেচ হবে।

দুটি Effect আলাদা—তাই একটার কাজ আরেকটাকে প্রভাবিত করবে না।


ব্যাখ্যা:

প্রতিটি useEffect যেন একটি স্বাধীন কাজ বা সিঙ্ক্রোনাইজেশন প্রসেস প্রতিনিধিত্ব করে। একাধিক "অলগাদা" কাজকে একসাথে রাখলে বাজে side effect হয়। বরং আলাদা করো—যাতে প্রতিটা Effect নিজ নিজ ডিপেনডেন্সি অনুযায়ী ঠিকভাবে কাজ করে।


যদি কোড ডুপ্লিকেট মনে হয়?

তাহলে তুমি fetchData(url, setter) নামে একটা custom hook বানিয়ে দুটো Effect-ই সুন্দরভাবে সংক্ষেপ করতে পারো। চাইলে আমি সেটা দেখাতেও পারি।


Md. Mojnu Miah, নিচে Section 2.3: Are you reading some state to calculate the next state? সহজ বাংলায় ব্যাখ্যা করছি। এই অংশটা React এর useEffect আর useState-এর গভীর কিন্তু দরকারি একটা টপিক—যেটা প্রায়ই মানুষ ভুল করে।


সেকশন ২.৩: আপনি কি বর্তমান স্টেট পড়ে পরবর্তী স্টেট নির্ধারণ করছেন?

চল, একটা ChatRoom উদাহরণ দেখি:

const [messages, setMessages] = useState([]);
 
useEffect(() => {
  const connection = createConnection();
  connection.connect();
 
  connection.on("message", (receivedMessage) => {
    setMessages([...messages, receivedMessage]); // ❌ ভুল
  });
 
  return () => connection.disconnect();
}, [roomId, messages]); // 🔴 সমস্যার মূল

এখানে যতবার একটা নতুন মেসেজ আসে, messages এর পুরনো কপি নিয়ে নতুন একটা অ্যারে বানিয়ে সেট করা হচ্ছে।


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

setMessages([...messages, receivedMessage]) লাইনটায় তুমি messages রিড করছো—মানে React-এর রি-অ্যাক্টিভ ভ্যালু Access করছো। তাই messages কে [messages] হিসেবে ডিপেনডেন্সি লিস্টে রাখতে হচ্ছে।

কিন্তু এর ফলাফল কী হয়?

  • নতুন মেসেজ আসলেই setMessages() কল হয় → Component রি-রেন্ডার হয়।
  • messages আপডেট হয় → useEffect আবার চালু হয়।
  • Effect চালু হলে → createConnection() আবার চালু হয়।
  • আবার কানেকশন, আবার কানেকশন...

💥 ফলাফল: প্রতিটি মেসেজে চ্যাট রুম বারবার কানেক্ট-ডিসকানেক্ট হয়। এইটা খুব বাজে ইউজার এক্সপেরিয়েন্স!


✅ সঠিক সমাধান: updater ফাংশন ব্যবহার করো

setMessages((msgs) => [...msgs, receivedMessage]);

পুরো কোডটা এভাবে লিখো:

useEffect(() => {
  const connection = createConnection();
  connection.connect();
 
  connection.on("message", (receivedMessage) => {
    setMessages((msgs) => [...msgs, receivedMessage]); // ✅ ভালো
  });
 
  return () => connection.disconnect();
}, [roomId]); // ✅ এখন messages দরকার নেই

এখন আর messages রিড করছো না, তাই ডিপেনডেন্সি লিস্টেও রাখার দরকার নেই। React নিজেই msgs আর্গুমেন্টে আগের messages দিয়ে দেবে। এটাকে বলে functional updater


কেন এটা কাজ করে?

React state আপডেট করার সময়, যদি তুমি ফাংশন দাও (prev => newValue), React তখনই পুরনো ভ্যালু ধরে তোমার ফাংশনটা চালায়। তাই তোমাকে পুরনো messages নিয়ে চিন্তা করতে হয় না।


বাংলায় সারাংশ:

যদি তোমার state আপডেট করতে পুরনো state দরকার হয়, তাহলে সরাসরি রিড না করে setState(prev => new) স্টাইলে লেখো। এতে করে useEffect বারবার চলবে না, কোড পারফরম্যান্স ভালো থাকবে, আর ইউজারের অভিজ্ঞতা মসৃণ হবে।


Md. Mojnu Miah, নিচে Section 2.4 — "Do you want to read a value without 'reacting' to its changes?" — এর সহজ ভাষায় ব্যাখ্যা করছি। এটি React-এর একটি একটু অ্যাডভান্স কনসেপ্ট যা future API useEffectEvent নিয়ে আলোচনা করে। আমরা ধাপে ধাপে বুঝব।


🔎 আপনি কি কোনো ভ্যালু পড়তে চান কিন্তু তার পরিবর্তনের প্রতিক্রিয়া (react) দিতে চান না?

React এর useEffect সাধারণত reactive হয়—মানে যেসব মান (state/prop) তুমি effect এর ভিতরে পড়ো, সেগুলোকে dependency list-এ রাখতে হয়।

কিন্তু অনেক সময় তুমি এমন মান (state) পড়তে চাও যার পরিবর্তনে তোমার effect আবার চালু হওয়া উচিত না।


🎯 উদাহরণ দিয়ে বোঝাই

ধরি, তুমি চাও:

যখন নতুন মেসেজ আসে, তখন একটা শব্দ বাজবে (🔔), কিন্তু যদি isMuted === true হয়, তাহলে বাজবে না।

const [isMuted, setIsMuted] = useState(false);
 
useEffect(() => {
  connection.on("message", (msg) => {
    setMessages((msgs) => [...msgs, msg]);
    if (!isMuted) {
      playSound(); // 🔔
    }
  });
}, [roomId, isMuted]); // ⚠️ এই isMuted এর জন্য effect আবার চলে!

এখানে সমস্যা:

  • isMuted বদলালেই, পুরো useEffect আবার চলবে!
  • অর্থাৎ chat রি-সংযোগ (reconnect) হবে।
  • এটা অপ্রয়োজনীয় রি-রান এবং ইউজার এক্সপেরিয়েন্স নষ্ট করে।

✅ সমাধান: useEffectEvent

React একটি নতুন (experimental) API দিয়েছে: useEffectEvent এটি এমন ফাংশন তৈরি করে, যা "সর্বশেষ স্টেট/প্রপস পড়তে পারে কিন্তু effect রি-রান করায় না"।

const onMessage = useEffectEvent((receivedMessage) => {
  setMessages((msgs) => [...msgs, receivedMessage]);
  if (!isMuted) {
    playSound(); // 🔔 কিন্তু এখন effect-reactive না!
  }
});

এবার useEffect এর ভিতরে শুধুমাত্র roomId পরিবর্তনে কাজ হবে:

useEffect(() => {
  const connection = createConnection();
  connection.connect();
  connection.on("message", (receivedMessage) => {
    onMessage(receivedMessage);
  });
  return () => connection.disconnect();
}, [roomId]); // ✅ আর isMuted নাই!

💡 সারমর্ম:

তুমি যদি useEffect এর ভিতরে কোন মান (state/prop) পড়ো, এবং তার পরিবর্তনেও effect চালু হোক না চাও, তাহলে সেই লজিক useEffectEvent() এর ভিতরে রাখো।


🎁 Bonus: প্রপস ফাংশনের জন্যও একই সমাধান

ধরো তোমার প্যারেন্ট কম্পোনেন্ট onReceiveMessage নামে একেকবার একেক ফাংশন পাঠায়:

<ChatRoom onReceiveMessage={(msg) => { ... }} />

React ধরে নেয়:

"এই ফাংশন তো নতুন! তাই effect আবার চালাও।"

সমাধান:

const onMessage = useEffectEvent((receivedMessage) => {
  onReceiveMessage(receivedMessage);
});

এভাবে effect ফাংশন রি-রান হবে না বারবার।


🔁 আলাদা করা: Reactive vs Non-Reactive Logic

Reactive partuseEffect (depends on things like roomId) Non-reactive partuseEffectEvent (reads things like isMuted or latest props without causing rerun)

আরেকটি উদাহরণ:

const onVisit = useEffectEvent((roomId) => {
  logVisit(roomId, notificationCount); // শুধু পড়ছি, dependency না
});
 
useEffect(() => {
  onVisit(roomId); // শুধুমাত্র roomId পরিবর্তনে কাজ
}, [roomId]); // ✅ notificationCount কে ডিপেনডেন্সি করা লাগছে না

🧠 মনে রাখার সহজ নিয়ম:

Effect-এর ভিতরে কোনো state বা prop পড়লে সেটা ডিপেন্ডেন্সিতে দিতে হয়। কিন্তু সেটা না চাইলে, লজিকটা useEffectEvent এ রাখো।


Here’s a simple explanation in Bangla for Section 3.5: "Does some reactive value change unintentionally?"


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

আপনি চাচ্ছেন useEffect() চলুক যখন roomId বদলাবে। কিন্তু আপনি নিচের মতো options নামে একটি object বানিয়েছেন:

const options = {
  serverUrl: serverUrl,
  roomId: roomId,
};

এবং এটি useEffect-এর মধ্যে ব্যবহার করছেন ও dependency হিসেবে সেট করেছেন:

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

❌ সমস্যার ফলাফল

এই options object টি প্রতিবার render হলে নতুন করে তৈরি হয়, কারণ এটি component-এর ভিতরে লেখা হয়েছে।

JavaScript-এ দুইটা object যদি দেখতে একরকম হলেও, তারা দুইটা আলাদা entity হয়:

const a = { roomId: "music" };
const b = { roomId: "music" };
console.log(Object.is(a, b)); // false

এই কারণে options সবসময় নতুন হিসেবে ধরা পড়ে, এবং useEffect বারবার rerun হয়ে যায়।

ফলাফল? আপনি শুধু input box-এ টাইপ করলেন, কিন্তু chat connection বারবার disconnect আর reconnect হচ্ছে! এটা আপনি চান না।


✅ সমাধান কী?

🔹 সমাধান 1: options কে সরিয়ে ফেলুন dependency থেকে

এবং ভেতরে roomId আর serverUrl ব্যবহার করুন:

useEffect(() => {
  const connection = createConnection({
    serverUrl: serverUrl,
    roomId: roomId,
  });
  connection.connect();
  return () => connection.disconnect();
}, [serverUrl, roomId]); // এখন primitive value dependency

এখানে object টা effect এর ভিতরেই তৈরি হচ্ছে, তাই প্রতিবার নতুন object হলেও সেটার কারণে useEffect rerun হবে না — কারণ dependency তে আমরা শুধু primitive values রেখেছি।


🔹 সমাধান 2: useMemo দিয়ে memoize করুন

যদি আপনি object বাইরে তৈরি করতেই চান, তাহলে useMemo ব্যবহার করে React-কে বলুন object টি তখনই তৈরি করতে যখন roomId বা serverUrl বদলাবে:

const options = useMemo(
  () => ({
    serverUrl: serverUrl,
    roomId: roomId,
  }),
  [serverUrl, roomId]
);

এখন এই options object ততক্ষণ একই থাকবে যতক্ষণ না roomId বা serverUrl বদলায়।


✅ মূল শিক্ষা

  • Object / Function কে useEffect এর dependency বানালে প্রতি render-এ তারা নতুন হিসেবে ধরা পড়ে।

  • ফলে Unnecessary Effect reruns হয়।

  • সমাধান:

    • primitive values ব্যবহার করুন [roomId, serverUrl]
    • অথবা useMemo দিয়ে object/function memoize করুন।

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

"useEffect-এর dependency list-এ object/function রাখার সময় সাবধান। তারা দেখতে এক হলেও প্রতিবার নতুন হয়!"


এখানে Section 3.5.1 এর সহজ বাংলায় ব্যাখ্যা দেওয়া হলো:


🧩 3.5.1: Static Object/Function হলে Component এর বাইরে রাখুন

🎯 সমস্যা মনে আছে?

আগের সেকশনে আমরা দেখেছি: useEffect() এর ভিতরে যদি object/function ব্যবহার করি, আর সেটা প্রতি render-এ নতুন হয়, তাহলে effect বারবার rerun হয়।

✅ সমাধান ১: যদি object পরিবর্তন না হয়, তাহলে component-এর বাইরে নিয়ে যান

// Component-এর বাইরে রাখায় এটি প্রতি render-এ নতুন হচ্ছে না
const options = {
  serverUrl: "https://localhost:1234",
  roomId: "music",
};
 
function ChatRoom() {
  const [message, setMessage] = useState("");
 
  useEffect(() => {
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, []); // ✅ dependency-তে কিছু দিতে হয়নি
}

🔍 এখানে options object টি একদম উপরে রাখা হয়েছে, কারণ এটা props বা state-এর উপর নির্ভর করে না।

ফলে React বুঝতে পারছে – এটি কোনো reactive value না, মানে render এর সাথে এর কোনো সম্পর্ক নেই।

✅ সমাধান ২: যদি কোনো function দিয়ে object তৈরি করেন, তাকেও component-এর বাইরে নিয়ে যান:

function createOptions() {
  return {
    serverUrl: "https://localhost:1234",
    roomId: "music",
  };
}
 
function ChatRoom() {
  const [message, setMessage] = useState("");
 
  useEffect(() => {
    const options = createOptions();
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, []); // ✅ dependency-তে কিছু দিতে হয়নি
}

🔍 এখানে createOptions() নামে একটা function ব্যবহার করা হয়েছে object তৈরি করতে, এবং যেহেতু এই function component এর বাইরে, তাই এটা প্রতিবার নতুন হিসেবে গণ্য হয় না।


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

যদি কোনো object বা function একেবারে static হয় — মানে props বা state এর উপর নির্ভর না করে — তাহলে সেটা component-এর বাইরে নিয়ে যান। এতে useEffect rerun থেকে বাঁচবে।


নিচে Section 3.5.2 এর সহজ বাংলায় ব্যাখ্যা ও সারাংশ দেওয়া হলো:


🧩 3.5.2: Dynamic object/function হলে Effect এর ভিতরে রাখুন

🧠 সমস্যার মূল কথা মনে করুন:

আমরা আগেই বলেছি — যদি আপনি useEffect() এর ভিতরে কোনো object/function দেন, আর সেটা প্রতিবার render-এ নতুন হয়, তাহলে Effect বারবার rerun হয়।

🎯 এখন যদি object/function কোনও reactive value (যেমন roomId) এর উপর নির্ভর করে, তাহলে আপনি সেটিকে component-এর বাইরে নিতে পারবেন না।

কিন্তু আপনি সেটা Effect এর ভিতরে রেখে সমাধান করতে পারেন।


✅ সমাধান: useEffect এর ভিতরে object/function তৈরি করুন

const serverUrl = "https://localhost:1234";
 
function ChatRoom({ roomId }) {
  const [message, setMessage] = useState("");
 
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId,
    };
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // ✅ এখন roomId পরিবর্তন হলে Effect rerun হবে
}

🔍 এখানে options object তৈরি করা হয়েছে useEffect-এর ভিতরে

তাই এটা আর dependency নয়। এখন শুধু roomId dependency, আর roomId যদি না বদলায়, তাহলে Effect আর rerun হবে না।


✅ একই কৌশল function এর জন্যও কাজ করে:

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState("");
 
  useEffect(() => {
    function createOptions() {
      return {
        serverUrl: "https://localhost:1234",
        roomId: roomId,
      };
    }
 
    const options = createOptions();
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // ✅ একইভাবে কাজ করে
}

🔍 যেহেতু createOptions() function-টি effect-এর ভিতরে, তাই সেটিও dependency নয়।


📌 মনে রাখুন:

🔁 আপনার object/function যদি reactive value (যেমন props, state) এর উপর নির্ভর করে, তাহলে সেই object/function effect-এর ভিতরে রাখুন — বাইরে রাখবেন না।

⚠️ বাইরে রাখলে প্রতিবার নতুন হিসেবে গণ্য হবে, আর effect বারবার rerun হবে।


✍️ সংক্ষেপে সারাংশ:

পরিস্থিতিকরণীয়
object/function staticcomponent-এর বাইরে নিয়ে যান
object/function dynamic (reactive value এর উপর নির্ভরশীল)effect-এর ভিতরে নিয়ে যান

নিচে Section 3.5.2 এর উপ-সেকশন “Calculate primitive values from functions” এর সহজ ভাষায় ব্যাখ্যা ও সারাংশ দেওয়া হলো:


🔢 3.5.2: ফাংশন থেকে প্রিমিটিভ মান বের করে নিন

🎯 সমস্যার ধরন:

ধরুন আপনি কোনো parent component থেকে একটি function পাঠান ChatRoom কম্পোনেন্টে, যেটা প্রতিবার নতুন object return করে:

<ChatRoom
  roomId={roomId}
  getOptions={() => {
    return {
      serverUrl: serverUrl,
      roomId: roomId,
    };
  }}
/>

এখানে getOptions প্রতিবার নতুন function তৈরি হচ্ছে — তাই useEffect এর dependency হিসেবে দিলে, React বারবার effect চালাবে।


✅ সমাধান:

👉 আপনি getOptions() ফাংশনটা component body-তে একবার কল করুন এবং তার থেকে primitive values (যেমন string) বের করে নিন।

function ChatRoom({ getOptions }) {
  const [message, setMessage] = useState("");
 
  const { roomId, serverUrl } = getOptions(); // 🎯 এখানে কল করা হয়েছে
  useEffect(() => {
    const connection = createConnection({ roomId, serverUrl });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, serverUrl]); // ✅ এখন dependency এ কোন object/function নেই
}

📌 কেন এটা কাজ করে?

  • roomIdserverUrl হলো primitive values (string), যেগুলো content দিয়ে তুলনা করা যায়।
  • তাই আপনি safe ভাবে এগুলোকে dependency হিসেবে দিতে পারেন।
  • আর getOptions() call হলো pure function, তাই render time-এ একবার call করলে সমস্যা নেই।

⚠️ কবে এটা করবেন না?

এই কৌশল কেবল তখনই ব্যবহার করুন যখন:

  • getOptions() হলো pure function — মানে এটি কোনো external effect ছাড়াই একই input এ একই output দেয়।
  • আপনি শুধু এর থেকে string, number বা boolean বের করছেন — object/function না

যদি getOptions() কোনো event handler বা async call-এর মত side effect করে থাকে, তাহলে একে effect-এর ভিতরে ব্যবহার করা উচিত।


✅ সারাংশ Recap:

সমস্যার ধরনসমাধান
প্রতি render-এ নতুন object return করে এমন ফাংশনuseEffect এর বাইরে ফাংশন কল করে প্রিমিটিভ মান বের করুন
ফাংশন থেকে object return করছেন এবং সেটা dependency এ দিলে effect বারবার চলেobject ভেঙে string/number নিয়ে dependency এ দিন
ফাংশন pure না হলে বা side effect করেফাংশন useEffect বা event handler-এ রাখুন

📘 টেকনিক্যাল দিক:

"To remove a dependency, you need to prove to the linter that it's not necessary."

React strict-ভাবে dependency দেখে, কারণ সে চায় আপনি predictable হোন। তাই dependency থেকে object/function বাদ দিতে চাইলে আগে এটা প্রমাণ করতে হবে — "এই object/function rerender-এ বদলায় না।"


💡 চ্যালেঞ্জের আগে মনে রাখুন:

🔁 Object/function প্রতিবার render-এ নতুন হয়। তাই যেখানেই সম্ভব, এগুলোকে:

  • Component এর বাইরে রাখুন (যদি static হয়)
  • অথবা Effect এর ভিতরে রাখুন (যদি dynamic/reactive হয়)
  • অথবা call করে তার থেকে string/number/boolean বের করে নিন

নিচে Section 3.5.3 এর ব্যাখ্যা দেওয়া হলো সহজ ভাষায়, যাতে আপনি সহজে বুঝতে পারেন কী সমস্যা হচ্ছিল এবং সেটা কীভাবে ঠিক করা হয়েছে:


🛠️ 3.5.3: Fix a resetting interval

😕 সমস্যা:

নিচের এই কোডে useEffect প্রতি সেকেন্ডে একটি interval সেট করে count বাড়ানোর জন্য:

useEffect(() => {
  const id = setInterval(() => {
    setCount(count + 1);
  }, 1000);
  return () => clearInterval(id);
}, [count]);

❗ কিন্তু এখানে একটা বড় সমস্যা হচ্ছে—count dependency হওয়ায় প্রতি টিক-এ useEffect আবার চলে, পুরনো interval ক্লিয়ার হয় এবং নতুন interval তৈরি হয়।

🔁 ফলে interval বারবার destroy → re-create হচ্ছে, যেটা performance খারাপ করে এবং আপনার উদ্দেশ্য সফল হচ্ছে না।


✅ সমাধান:

👉 সমাধান হলো: count + 1 না লিখে, setCount(c => c + 1) লিখুন।

এইভাবে আপনি count এর আগের মানটা React-এর হাতে ছেড়ে দিচ্ছেন, এবং useEffect এর ভিতরে count directly use করা লাগছে না — তাই dependency array খালি রাখতে পারেন।

Final Code:

import { useState, useEffect } from "react";
 
export default function Timer() {
  const [count, setCount] = useState(0);
 
  useEffect(() => {
    console.log("✅ Creating an interval");
    const id = setInterval(() => {
      console.log("⏰ Interval tick");
      setCount((c) => c + 1); // ✅ updater function ব্যবহার
    }, 1000);
    return () => {
      console.log("❌ Clearing an interval");
      clearInterval(id);
    };
  }, []); // ✅ count আর dependency নয়
 
  return <h1>Counter: {count}</h1>;
}

📘 কেন এটা কাজ করে?

setCount(c => c + 1) এটা একটা updater function React নিজে পুরনো count ধরে নিয়ে তার উপর +1 করে state আপডেট করে।

এখন যেহেতু count আর effect-এর ভিতরে সরাসরি ব্যবহার হয়নি, তাই useEffect আর count এর উপর নির্ভরশীল না — তাই effect শুধু একবার চলে, আবার বারবার destroy-create করে না।


🔁 আগের বনাম পরের পার্থক্য:

আগের কোড (ভুল)পরের কোড (সঠিক)
setCount(count + 1)setCount(c => c + 1)
dependency: [count]dependency: []
প্রতি সেকেন্ডে effect আবার চলেeffect একবারই চলে
interval বারবার destroy-createinterval বারবার তৈরি হয় না

🔍 টিপস মনে রাখুন:

  • যদি আপনার useEffect এর ভিতরে কোনো state (যেমন count) ব্যবহার করতে হয়, এবং সেটা আগের মানের উপর ভিত্তি করে আপডেট করতে হয়— তাহলে updater function ব্যবহার করুন।
  • এতে করে আপনি dependency array-এ সেই মান রাখতে বাধ্য হবেন না, আর অপ্রয়োজনীয় effect চলাও বন্ধ হবে।

Here's a simple, beginner-friendly breakdown of the key idea in Section 3.5.5: Fix a retriggering animation, tailored for your React Bangla Tutorial book:


✅ Section 3.5.5: Fix a Retriggering Animation (Bangla Friendly Guide)

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

আমরা একটা কম্পোনেন্ট বানিয়েছি যেটা Welcome মেসেজকে fade-in animation দিয়ে দেখায়। duration slider দিয়ে এনিমেশন কত সেকেন্ড চলবে সেটা নিয়ন্ত্রণ করা যায়।

❌ কিন্তু যখনই আমরা slider ঘুরাই (duration পরিবর্তন করি), তখন পুরো এনিমেশন আবার শুরু হয়ে যায়—even যদি কম্পোনেন্ট আগেই দেখানো থাকে!

আমরা চাই:

  • এনিমেশন শুধু তখন শুরু হোক যখন “Show” বাটন চাপা হয়, কিন্তু
  • duration পরিবর্তন করলে এনিমেশন পুনরায় শুরু না হয়

🔍 সমস্যার কারণ কী?

useEffect(() => {
  const animation = new FadeInAnimation(ref.current);
  animation.start(duration);
  return () => animation.stop();
}, [duration]);

এখানে [duration] dependency list-এ আছে, মানে duration পরিবর্তন হলেই effect আবার চলে, এবং এনিমেশন রিস্টার্ট হয়।


🛠️ সমাধান: useEffectEvent ব্যবহার করি

আমরা animation শুরু করার কাজটাকে একটা non-reactive function-এ রাখব, যেটা শুধু একবার চলবে।

const onAppear = useEffectEvent((animation) => {
  animation.start(duration); // এখানেও আমরা duration পড়ছি, কিন্তু এটা reactive না
});

তারপর useEffect এর ভেতর থেকে এই ফাংশনটা কল করব:

useEffect(() => {
  const animation = new FadeInAnimation(ref.current);
  onAppear(animation); // শুধুমাত্র একবার চলবে
  return () => animation.stop();
}, []);

✨ এখন যা হচ্ছে:

  • যখন “Show” চাপা হয়, Welcome কম্পোনেন্ট মাউন্ট হয় => useEffect রান হয় => এনিমেশন শুরু হয়।
  • এরপর যতবার duration পরিবর্তন করি না কেন, এনিমেশন আর শুরু হয় না।

✅ চূড়ান্ত কোড (Welcome.jsx):

function Welcome({ duration }) {
  const ref = useRef(null);
 
  const onAppear = useEffectEvent((animation) => {
    animation.start(duration);
  });
 
  useEffect(() => {
    const animation = new FadeInAnimation(ref.current);
    onAppear(animation);
    return () => animation.stop();
  }, []);
 
  return (
    <h1 ref={ref} style={{ opacity: 0, ...otherStyles }}>
      Welcome
    </h1>
  );
}

📘 মনে রাখুন:

  • যদি কোনো effect শুধু একবারই চলা দরকার হয়, তাহলে [duration] বা অন্য reactive মানগুলো avoid করুন।
  • React 19 এর useEffectEvent() আপনাকে এমন non-reactive function বানাতে দেয়, যেটা effect এর ভেতর ব্যবহার করা যায়—but dependency হিসেবে react করে না।

অবশ্যই, মজনু ভাই! আপনার মতো প্রাঞ্জল ও সহজ ভাষায় React বইয়ের চ্যাপ্টার শুরু করা যাক, যেন কারো কাছে ক্লান্তিকর না লাগে। নিচে আমি আপনার দেওয়া ইংরেজি টেক্সটের মতো সোজাসাপ্টা বাংলা ভাষায় লেখা দিয়েছি, যেন নতুনদের জন্য বুঝতে সহজ হয়, আর অনুবাদের ছাপ কম থাকে।


চ্যাট আবার রিকনেক্ট করছে? সমস্যা আর সমাধান

এই উদাহরণে, যখনই আপনি “Toggle theme” বাটনে চাপেন, তখন চ্যাট রুম আবার রিকনেক্ট করে। কিন্তু আসলে এর কারণ কী?

আমরা চাই, চ্যাট রুম তখনই রিকনেক্ট করুক যখন:

  • আপনি সার্ভার URL বদলান, বা
  • চ্যাট রুম পরিবর্তন করেন।

আর “Toggle theme” করার সময় চ্যাট রিকনেক্ট না করুক।


কী সমস্যা হচ্ছে?

ChatRoom কম্পোনেন্টে একটা options নামের অবজেক্ট দেওয়া হচ্ছে। React-এ যখনই useEffect-এর ডিপেন্ডেন্সিতে options অবজেক্ট থাকে, React ধরতে পারে না যে আসলে options বদলেছে কি না, কারণ প্রতিবার নতুন অবজেক্ট তৈরি হয়। তাই useEffect বারবার চলে — যার ফলে চ্যাট বারবার রিকনেক্ট করে।


সমাধান কী?

আমরা চাই useEffect শুধু তখনই চালাক, যখন serverUrl বা roomId সত্যিই বদলায়।

এই জন্য, useEffect-এর ডিপেন্ডেন্সিতে সরল (primitive) মানগুলো রাখা ভালো — যেমন roomId এবং serverUrl — যেন React ঠিক বুঝতে পারে কখন কাজটা চালাতে হবে।


কোড দেখে নেওয়া যাক

import { useEffect } from "react";
import { createConnection } from "./chat.js";
 
export default function ChatRoom({ options }) {
  // options থেকে আলাদা করে primitive মানগুলো নেয়া হলো
  const { roomId, serverUrl } = options;
 
  useEffect(() => {
    // এখানে আমরা নতুন অবজেক্ট তৈরি করে createConnection-এ পাঠাচ্ছি
    const connection = createConnection({ roomId, serverUrl });
    connection.connect();
 
    return () => connection.disconnect();
  }, [roomId, serverUrl]); // ডিপেন্ডেন্সি হিসেবে শুধু primitive মানগুলো দিলাম
 
  return <h1>Welcome to the {roomId} room!</h1>;
}

আরও ভালো উপায়

আপনি চাইলে ChatRoom-এর props থেকে সরাসরি roomId এবং serverUrl নিতে পারেন, অর্থাৎ options অবজেক্টের বদলে আলাদা আলাদা প্রপস ব্যবহার করতে পারেন — এটা React কে বুঝতে সাহায্য করে কোন প্রপস বদলেছে।

export default function ChatRoom({ roomId, serverUrl }) {
  useEffect(() => {
    const connection = createConnection({ roomId, serverUrl });
    connection.connect();
 
    return () => connection.disconnect();
  }, [roomId, serverUrl]);
 
  return <h1>Welcome to the {roomId} room!</h1>;
}

শেষ কথা

  • React-এ useEffect-এর ডিপেন্ডেন্সিতে কখনও অবজেক্ট বা অ্যারেও দেবেন না, কারণ এগুলো প্রতিবার নতুন ইনস্ট্যান্স হয়।
  • সেজন্য যতটা সম্ভব প্রিমিটিভ ভ্যালু (string, number, boolean) ব্যবহার করুন।
  • এতে আপনার কোড দ্রুত, সহজ এবং বাগ মুক্ত হবে।