অধ্যায়: ইফেক্টের অপ্রয়োজনীয় ডিপেন্ডেন্সি সরানো

শুরুতেই বুঝে নিই

React-এ যখন আপনি useEffect() ব্যবহার করেন, তখন একটা ব্যাপার খেয়াল করতে হয় — আপনার ইফেক্ট কোন কোন প্রপ বা স্টেট ভ্যালু ব্যবহার করছে।

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

যদি আপনি কিছু বেশি দেন, তবে ইফেক্ট বারবার চলতে পারে। আর যদি কম দেন, তাহলে সঠিকভাবে সিঙ্ক হবে না।

এই অধ্যায়ে আমরা শিখব—

  • কখন কোন ডিপেন্ডেন্সি রাখা জরুরি
  • কখন কোনটা বাদ দেওয়া উচিত
  • আর কিভাবে ভুলভাবে দেয়া ডিপেন্ডেন্সির কারণে বারবার ইফেক্ট চালু হওয়া বা লুপ হওয়া বন্ধ করা যায়

এই অধ্যায় থেকে আপনি যা শিখবেন:

  • ✅ ইফেক্ট যদি বারবার চলে — সেই লুপ থেকে কিভাবে বাঁচবেন
  • ✅ আপনি যদি চান কোনো ডিপেন্ডেন্সি বাদ দিতে — তখন কী করবেন
  • ✅ কিভাবে কোনো ভ্যালু পড়বেন, কিন্তু সেটার উপর ইফেক্ট চালু হবে না
  • ✅ অবজেক্ট বা ফাংশনকে ডিপেন্ডেন্সি হিসেবে না রাখার কারণ ও সমাধান
  • eslint-disable-next-line দিয়ে লিন্টার চুপ করানো কেন বিপদজনক — আর তার বদলে কী করবেন

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


ডিপেনডেন্সি ঠিকঠাক দিলে তবেই ইফেক্ট ঠিকমতো চলবে

React-এ যখন আপনি useEffect() ব্যবহার করেন, তখন আপনি বলেন— “এই ইফেক্টটা শুরু হবে কখন, আর বন্ধ হবে কখন।”

উদাহরণ দেখি:

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

উপরে আমরা useEffect() লিখেছি ঠিকই, কিন্তু একটা ভুল করে ফেলেছি — আমরা roomId-কে ডিপেনডেন্সি হিসেবে দিইনি!


ডিপেনডেন্সি খালি রাখলে কী হয়?

যদি আপনি ডিপেনডেন্সি অ্যারে খালি রাখেন ([]), তাহলে React ভাববে — “এই ইফেক্টটা শুধু একবারই চলবে, যতবারই roomId বদলাক না কেন।”

ফলে ইউজার চ্যাট রুম বদলালেও, আগের রুমেই কানেক্টেড থাকবে — এবং এটা তো একদম ভুল ব্যবহারকারীর অভিজ্ঞতা!


চলুন ভুলটা দেখি 👇

useEffect(() => {
  const connection = createConnection(serverUrl, roomId);
  connection.connect();
  return () => connection.disconnect();
}, []); // ❌ roomId দেওয়া হয়নি

React-এর লিন্টার (যেটা ভুল ধরতে সাহায্য করে) তখন বলবে — “ভাই, তুমি roomId ব্যবহার করেছো, কিন্তু ডিপেনডেন্সিতে দাওনি।”


তাহলে সঠিকটা কী হবে?

useEffect(() => {
  const connection = createConnection(serverUrl, roomId);
  connection.connect();
  return () => connection.disconnect();
}, [roomId]); // ✅ ঠিকভাবে দেওয়া হয়েছে

এখানে roomId যেহেতু বারবার বদলাতে পারে, তাই ইফেক্টকেও প্রতিবার নতুন রুমে কানেক্ট হতে হবে। React এটা বুঝে যাবে, কারণ আপনি roomId-কে ডিপেনডেন্সিতে দিয়েছেন।


পুরো কোড এক নজরে দেখুন 👇

function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);
 
  return <h1>Welcome to the {roomId} room!</h1>;
}
 
export default function App() {
  const [roomId, setRoomId] = useState("general");
 
  return (
    <>
      <label>
        কোন চ্যাট রুমে যাবেন?
        <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} />
    </>
  );
}

মনে রাখুন:

🧠 Effect “react” করে রিএকটিভ ভ্যালু’র উপর। আপনি যদি এমন কিছু ব্যবহার করেন যেটা বারবার বদলাতে পারে (যেমন: props বা state), তাহলে সেটা [roomId]-এর মতো ডিপেনডেন্সিতে অবশ্যই রাখতে হবে।

তাহলেই ইফেক্ট ঠিকমতো রিএক্ট করবে — নাহলে আপনার অ্যাপ ভুলভাবে কাজ করবে, কিন্তু আপনি বুঝতেও নাও পারেন!


✅ এখন আপনি জানেন — useEffect()-এ কোন ভ্যালু ডিপেনডেন্সিতে দিতে হবে, আর সেটা না দিলে কী ধরনের ভুল হতে পারে।

চমৎকার! এখন আমরা Section 1.1 – "To remove a dependency, prove that it's not a dependency" – প্রাঞ্জল ও সহজ বাংলায় লিখে শুরু করি যেন আপনার পাঠকরা একদম স্পষ্টভাবে বুঝতে পারে।


সেকশন ১.১: কোন ভ্যালু ইফেক্টের জন্য দরকার নয় — সেটা প্রমাণ করেই বাদ দিতে হয়

React-এ useEffect() এর ভেতরে যেসব মান (value) আপনি ব্যবহার করছেন, সেগুলোর ওপর ভিত্তি করেই dependency list তৈরি করতে হয়। মানে হচ্ছে, আপনি ইচ্ছেমতো ঠিক করতে পারবেন না কোনটা dependency হবে আর কোনটা হবে না।

যেসব ভ্যালু আপনি ইফেক্টের ভেতরে ব্যবহার করছেন — যদি সেগুলো React-এর চোখে “reactive value” হয়, তাহলে অবশ্যই আপনাকে সেগুলো dependency list-এ রাখতে হবে।

একটা সাধারণ উদাহরণ দেখি:

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

এখানে roomId আসছে props থেকে, আর যেহেতু props বা component-এর ভিতরে declare করা যেকোনো ভ্যালু reactive — তাই roomId-কে [roomId] হিসেবে dependency list-এ রাখতে হবে।

এটা না রাখলে React এর লিন্টার (linter) আপনাকে এরর দেবে।

ধরুন আপনি ভুল করে এভাবে লিখলেন:

function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, []); // 🔴 এখানে লিন্টার বলবে roomId মিসিং
}

এইখানে React ঠিক বলছে — কারণ roomId তো যে কোনও সময় বদলে যেতে পারে, তাই তাকে নজরে রাখতে হবে।


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

React-কে আপনাকে প্রমাণ করতে হবে — এই ভ্যালু কখনোই বদলাবে না।

তাহলে সে মানবে এইটা reactive না, আর dependency list-এ রাখতেও হবে না।

ধরুন আপনি roomId-কে component-এর বাইরে নিয়ে গেলেন:

const serverUrl = "https://localhost:1234";
const roomId = "music"; // এখন এটা reactive না
 
function ChatRoom() {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, []); // ✅ এখন dependency নেই, কারণ কোনও reactive value নাই
}

এখানে roomId আর serverUrl দুটোই component-এর বাইরে, মানে তারা কখনোই rerender এর সময় বদলাবে না।

তাই dependency list ফাঁকা ([]) রাখা একদম ঠিক আছে।


সহজ কথায় সারাংশ:

  • যেটা component-এর ভিতরে আসে (props, state, const) — সেটা reactive।
  • ইফেক্টে যেসব reactive মান ব্যবহার করবেন, সেগুলো dependency list-এ রাখতেই হবে।
  • যদি কোনো মানের ওপর ইফেক্ট নির্ভর না করে — তাহলে সেটাকে component-এর বাইরে নিয়ে যান।
  • তখন dependency list ফাঁকা রাখা যায়।

এখন চলুন, এই কোডটা দেখুন:

import { useEffect } from "react";
import { createConnection } from "./chat.js";
 
const serverUrl = "https://localhost:1234";
const roomId = "music";
 
export default function ChatRoom() {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, []);
 
  return <h1>Welcome to the {roomId} room!</h1>;
}

এখানে ইফেক্ট চলবে শুধু একবার — যখন component প্রথমবার render হবে। এর পর আর কোনো rerender-এ ইফেক্ট চলবে না — কারণ dependency নেই।


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


সেকশন ১.২: ডিপেনডেন্সি লিস্ট বদলাতে চাইলে, আগে কোড বদলান

React-এ useEffect ব্যবহার করতে গেলে একটা জিনিস খুবই জরুরি — ডিপেনডেন্সি লিস্ট ঠিকভাবে দেওয়া।

অনেক সময় এমন হয়, আপনি কোডে কিছু বদলান, তারপর লিন্টার কিছু একটা বলছে — "এই ভ্যালুটা ডিপেনডেন্সিতে দিতে হবে!" তখন আপনি হয়তো [] দিয়ে দিচ্ছেন, আবার কখনো // eslint-disable-next-line দিয়ে ওটা চেপে যাচ্ছেন।

কিন্তু এতে করে React এর কাজটা ভুলভাবে হয় — আর তার মানে বাগ আসতে পারে।

চলুন, আমরা একটা সাধারণ নিয়ম মাথায় রাখি:

ডিপেনডেন্সি লিস্ট আপনার কোডের বিবরণ দেয়। আপনি ইচ্ছেমতো ওখানে কিছু বসাতে পারবেন না।

তাহলে কি করবেন?

যদি ডিপেনডেন্সি লিস্ট আপনাকে পছন্দ না হয়, তাহলে প্রথমে কোডটাই বদলান। ডিপেনডেন্সি লিস্ট নিজে থেকে আসে — আপনার লেখা কোডে কোন কোন স্টেট বা প্রপ ব্যবহৃত হয়েছে, সেটার ভিত্তিতে।

তাই আপনি চাইলে [] দিয়ে চুপ করিয়ে দিতে পারেন, কিন্তু সেটি হলে React আপনার কোডকে ভুলভাবে বুঝে নেয়।

এটা অনেকটা অংকের সমীকরণ (equation) মেলানোর মতো। যে ভ্যালুগুলো আপনি ব্যবহার করছেন, সেগুলোকে React ধরতে পারে, আর সেটার ভিত্তিতে ডিপেনডেন্সি লিস্ট বানায়।


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

অনেক পুরনো প্রজেক্টে এমন কোড দেখা যায়:

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

এইভাবে [] দিয়ে সব চেপে ফেললে খুব ভয়ংকর বাগ আসতে পারে — React ভাবে, আপনার ইফেক্ট কোনও কিছুর ওপর নির্ভর করছে না! কিন্তু বাস্তবে তা না — আপনি তো অনেক কিছু ব্যবহার করছেন ভেতরে।


একটু গভীরে যাই: এই ভুলে কী সমস্যা হয়?

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

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}</h1>
      <button onClick={() => setCount(0)}>Reset</button>
      <hr />
      <p>
        প্রতি সেকেন্ডে বাড়বে:
        <button onClick={() => setIncrement((i) => i - 1)}>–</button>
        <b>{increment}</b>
        <button onClick={() => setIncrement((i) => i + 1)}>+</button>
      </p>
    </>
  );
}

এখানে আপনার মনে হতে পারে, “আমি তো চাই শুধু একবার চলুক (on mount), তাই [] দিলাম।” কিন্তু onTick() ফাংশনটা তো count আর increment ব্যবহার করছে। তাহলে সেটা প্রতি রেন্ডারে বদলাচ্ছে, তাই না?

কিন্তু আপনি [] দিয়ে React-কে বললেন, “না না, কিছু বদলালে ইফেক্ট চালাতে হবে না।” React তখন onTick-এর পুরোনো ভার্সন ধরে নেয়, যেটা শুধু count = 0, increment = 1 ধরে। তাই setCount(0 + 1) বারবার চলে, আর আপনি 1 থেকে আর বাড়তেই দেখছেন না।


তাহলে সমাধান কী?

  • onTick ফাংশনকে ডিপেনডেন্সি লিস্টে দিন, অথবা
  • onTick-কে Effect Event বানিয়ে দিন, যেন সেটা সবসময় সাম্প্রতিক (latest) ভ্যালু ব্যবহার করে।

📌 সবচেয়ে ভালো নিয়ম:

React লিন্টার যদি কিছু বলে, ওটা মানুন। ওটাকে কখনও চুপ করাবেন না।

কারণ এটা আপনাকে ভবিষ্যতের ভয়ংকর বাগ থেকে বাঁচায়।


চমৎকার! চলুন তাহলে আমরা সেকশন ২ শুরু করি ঠিক আগের স্টাইলেই — সরল, প্রাঞ্জল বাংলা ভাষায় যেন পাঠকের মনে হয়, বন্ধু পাশে বসে বুঝিয়ে দিচ্ছে।


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

React এ ইফেক্ট লেখার সময় আমরা প্রায়ই useEffect এর শেষে একটা dependency list দিই — যেমন [count] বা [user, isLoggedIn]

এই লিস্টটা দেখে React বুঝে, কোন ভ্যালু বদলালে ইফেক্টটা আবার চালাতে হবে।

কিন্তু সবসময় কি এই লিস্টে থাকা ভ্যালুগুলা বদলালেই ইফেক্ট আবার চালানো দরকার?

সবসময় না।

অনেক সময় এমন হয়, আমরা ভুল করে বেশি কিছু দিয়ে দিই। এতে ইফেক্ট বারবার অপ্রয়োজনীয়ভাবে চলে, যেটা পারফরম্যান্সের দিক থেকে ভালো না।

চলুন দেখি, কবে কবে এই রকম সমস্যা হয়:


⚠️ কখন dependency বাদ দেওয়া উচিত?

  1. 🔄 আপনি চান না সব সময় ইফেক্ট চালু হোক — বরং চান, ভিন্ন ভিন্ন অংশ চলুক ভিন্ন শর্তে

  2. 👁️ আপনি শুধু কোনো ভ্যালুর সর্বশেষ মানটা পড়তে চান — তার পরিবর্তনের উপর রিঅ্যাক্ট করতে চান না।

  3. ⚙️ কোনো dependency (যেমন ফাংশন বা অবজেক্ট) বারবার তৈরি হচ্ছে, তাই React ভাবছে এটা নতুন — আর বারবার ইফেক্ট চালাচ্ছে।


✅ এই সমস্যাগুলোর সমাধান খুঁজে পেতে হলে, আপনাকে কয়েকটা প্রশ্ন করতে হবে:

  • এই ইফেক্টটা কোন পরিস্থিতিতে চলা উচিত?
  • আমি কি পুরো ইফেক্ট চালাতে চাই, না কি এর কোনো অংশ চালানোই যথেষ্ট?
  • আমি কি ডিপেন্ডেন্সি বদলাতে চাই, না শুধু তার সর্বশেষ মানটা জানতে চাই?

চিন্তা করবেন না — আমরা এসব প্রশ্নের উত্তর এক এক করে আলোচনা করব। চলুন, এবার শুরু করি প্রশ্ন ধরে ধরে সমাধান খোঁজা।


চলুন ভাই, আমরা এই সেকশনটা সহজ, প্রাঞ্জল বাংলা ভাষায় সাজিয়ে লিখি, যেন React শেখার সময় কেউ একটুও কনফিউজড না হয়।


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

React-এ অনেক সময় এমন কিছু কোড থাকে যেটা আপনি হয়তো ইফেক্টের ভেতরে লিখে ফেলেছেন। কিন্তু আসলেই কি সেটা ইফেক্টের জন্য উপযুক্ত?

চলুন একটা বাস্তব উদাহরণ দেখি।


ধরুন আপনি একটা ফর্ম বানাচ্ছেন

এই ফর্মে ইউজার সাবমিট করলে আপনি submitted নামের একটা স্টেট true করে দেন। এরপর আপনি চাচ্ছেন দুইটা কাজ করতে:

  1. সার্ভারে একটা POST রিকোয়েস্ট পাঠাতে হবে
  2. আর ইউজারকে দেখাতে হবে “সাবমিট সফল হয়েছে” — এমন একটা নোটিফিকেশন

এই দুই কাজ আপনি নিচের মতো করে ইফেক্টে লিখে ফেললেন:

function Form() {
  const [submitted, setSubmitted] = useState(false);
 
  useEffect(() => {
    if (submitted) {
      // ⚠️ সমস্যা: ইভেন্ট ভিত্তিক কাজ ইফেক্টে রাখা ঠিক না
      post("/api/register");
      showNotification("Successfully registered!");
    }
  }, [submitted]);
 
  function handleSubmit() {
    setSubmitted(true);
  }
}

এ পর্যন্ত মনে হচ্ছে ঠিক আছে। কিন্তু এরপর আপনি ভাবলেন — নোটিফিকেশনের স্টাইল থিম অনুযায়ী দেখাবেন।

আপনি theme কনটেক্সট থেকে নিচ্ছেন, যেটা রিঅ্যাকটিভ ভ্যালু। তাই ইফেক্টের ডিপেন্ডেন্সি লিস্টে সেটাও যোগ করলেন:

function Form() {
  const [submitted, setSubmitted] = useState(false);
  const theme = useContext(ThemeContext);
 
  useEffect(() => {
    if (submitted) {
      // ⚠️ এখনও সমস্যা আছে
      post("/api/register");
      showNotification("Successfully registered!", theme);
    }
  }, [submitted, theme]);
 
  function handleSubmit() {
    setSubmitted(true);
  }
}

এবার সমস্যা হলো, ফর্ম একবার সাবমিট করার পর আপনি যদি থিম Dark থেকে Light করেন, তাহলে theme বদলাবে, আর ইফেক্ট আবার চলবে — ফলাফল: নোটিফিকেশন আবার দেখাবে!


সমস্যার মূল: এটা ইফেক্টে রাখার দরকারই ছিল না

এই কোডটা আপনি লিখেছিলেন ইউজার যখন সাবমিট করবে, তখন চালানোর জন্য। অর্থাৎ, এটা একদম স্পষ্টভাবে ইভেন্ট হ্যান্ডলারের কাজ।

তাই এই কোড ইফেক্টে না রেখে সরাসরি সাবমিট হ্যান্ডলারে রাখুন:

function Form() {
  const theme = useContext(ThemeContext);
 
  function handleSubmit() {
    // ✅ ঠিক আছে: ইভেন্ট হ্যান্ডলারে কাজ করা হচ্ছে
    post("/api/register");
    showNotification("Successfully registered!", theme);
  }
}

এখন যেহেতু কোডটা ইভেন্ট হ্যান্ডলারের মধ্যে আছে, 👉 এটা শুধু তখনই চলবে, যখন ইউজার ফর্ম সাবমিট করবে। 👉 থিম বদলালে এটা আর চলবে না।


মনে রাখবেন

যখন কোনো কোড ইউজারের নির্দিষ্ট একশনে চালাতে চান (যেমন সাবমিট, ক্লিক), তখন সেটা ইভেন্ট হ্যান্ডলারে রাখাই সেরা।

আর যেসব কোড আপনি চাইছেন স্টেট বা প্রপ বদলালেই চালু হোক, সেগুলো রাখবেন ইফেক্টে।


চলুন, এবার এই সেকশনটা আমরা একদম প্রাঞ্জল, সহজ বাংলায় লিখে ফেলি — যেন মনে হয় আপনি আপনার ছোট ভাইকে শিখিয়ে দিচ্ছেন। 😊


২.২: আপনার Effect কি একসাথে একাধিক আলাদা কাজ করছে?

React-এ যখন useEffect() ব্যবহার করি, তখন আমাদের জিজ্ঞেস করতে হয়— এই Effect টা কি একসাথে একাধিক ভিন্ন ভিন্ন কাজ করছে?

ধরুন একটা উদাহরণ

আপনি একটা শিপিং ফর্ম বানাচ্ছেন। এই ফর্মে ইউজারকে আগে দেশের নাম দিতে হবে। তারপর, সেই দেশের উপর ভিত্তি করে সিটি লিস্ট (dropdown) আসবে।

সেখান থেকে শুরু করি:

function ShippingForm({ country }) {
  const [cities, setCities] = useState(null);
  const [city, setCity] = useState(null);
 
  useEffect(() => {
    let ignore = false;
    fetch(`/api/cities?country=${country}`)
      .then((res) => res.json())
      .then((json) => {
        if (!ignore) {
          setCities(json);
        }
      });
    return () => {
      ignore = true;
    };
  }, [country]); // ✅ country বদলালে ফেচ চলবে
}

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


এবার আপনি দ্বিতীয় স্টেপে যাচ্ছেন

সিটি সিলেক্ট করার পরে, সেই সিটির উপর ভিত্তি করে এরিয়া লিস্ট আনতে চান। তাই ভাবলেন, একই Effect-এর মধ্যে আরেকটা fetch বসিয়ে দেন:

useEffect(() => {
  let ignore = false;
  fetch(`/api/cities?country=${country}`)
    .then((res) => res.json())
    .then((json) => {
      if (!ignore) {
        setCities(json);
      }
    });
 
  if (city) {
    fetch(`/api/areas?city=${city}`)
      .then((res) => res.json())
      .then((json) => {
        if (!ignore) {
          setAreas(json);
        }
      });
  }
 
  return () => {
    ignore = true;
  };
}, [country, city]);

এই কোডটা দেখতে ঠিক মনে হলেও, এখানে একটা লুকানো সমস্যা আছে।

এখন city-ও dependency তে যুক্ত হওয়ায়, city বদলালেও আবার cities ফেচ হচ্ছে — যেটা একদম অপ্রয়োজনীয়।


সমস্যাটা কী?

আপনি একসাথে দুইটা আলাদা কাজ করছেন:

  1. country বদলালে cities আনছেন।
  2. city বদলালে areas আনছেন।

এই দুইটা কাজ একে অপরের সাথে সম্পর্কিত না। তাই এদের জন্য আলাদা Effect হওয়া উচিত।


সমাধান: Effect কে ভাগ করে ফেলুন

function ShippingForm({ country }) {
  const [cities, setCities] = useState(null);
  const [city, setCity] = useState(null);
  const [areas, setAreas] = useState(null);
 
  // ✅ country বদলালে cities আনবে
  useEffect(() => {
    let ignore = false;
    fetch(`/api/cities?country=${country}`)
      .then((res) => res.json())
      .then((json) => {
        if (!ignore) {
          setCities(json);
        }
      });
    return () => {
      ignore = true;
    };
  }, [country]);
 
  // ✅ city বদলালে areas আনবে
  useEffect(() => {
    if (city) {
      let ignore = false;
      fetch(`/api/areas?city=${city}`)
        .then((res) => res.json())
        .then((json) => {
          if (!ignore) {
            setAreas(json);
          }
        });
      return () => {
        ignore = true;
      };
    }
  }, [city]);
}

আমরা কী শিখলাম?

  • একটা Effect-এ শুধু একটা কাজ থাকাই ভালো।
  • দুটো আলাদা জিনিস যদি আলাদা ভ্যালুর উপর নির্ভর করে, তাহলে তাদের জন্য দুটি আলাদা Effect লিখুন
  • এতে আপনার কোড ক্লিয়ার থাকবে, ভুল হওয়ার চান্স কমবে।

মনে রাখবেন: “একটা Effect = একটা সিনক্রোনাইজেশন” দুইটা আলাদা ডেটা ফেচ মানে দুইটা আলাদা সিনক্রোনাইজেশন। তাই দুইটা আলাদা Effect লিখুন।


আপনি চাইলে এখানে একটা custom Hook বানিয়ে দুটোই ছোট করে নিতে পারেন।

তবে সেটা পরের ধাপে আলোচনা করব।

অসাধারণ! এবার চলুন আমরা সেকশন ২.৩"আপনি কি পুরনো স্টেট ব্যবহার করে নতুন স্টেট হিসাব করছেন?" – একদম প্রাঞ্জল ও সহজ বাংলায় লিখে ফেলি, যেন React শেখা হয় উপভোগ্য, কোনো অনুবাদের গন্ধ ছাড়াই।


২.৩: পুরনো স্টেট পড়ে যদি নতুন স্টেট বানাতে হয়?

অনেক সময় এমন হয়, আপনি কোনো নতুন ডেটা পেয়েছেন — সেটা আগে থাকা ডেটার শেষে যোগ করতে চান।

ধরুন, আপনি চ্যাট অ্যাপ বানাচ্ছেন। নতুন মেসেজ এলে সেটা messages নামের স্টেটে যোগ করছেন। নিচে এর কোডটা দেখা যাক:

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);
 
  useEffect(() => {
    const connection = createConnection();
    connection.connect();
 
    connection.on("message", (receivedMessage) => {
      setMessages([...messages, receivedMessage]);
    });
 
    return () => connection.disconnect();
  }, [roomId, messages]);
}

এখানে আপনি যা করছেন:

  • পুরনো messages অ্যারে নিচ্ছেন
  • নতুন মেসেজটাকে তার শেষে যোগ করছেন
  • তারপর setMessages() দিয়ে নতুন অ্যারে সেট করছেন

এটা দেখতে ঠিকঠাক মনে হলেও একটা সমস্যা তৈরি হচ্ছে...


সমস্যা: বারবার কানেকশন রিফ্রেশ হচ্ছে

আপনি যখন setMessages() চালান, তখন React আবার রেন্ডার করে, এবং messages নতুন মান পায়।

কিন্তু আপনার useEffect() তো messages এর ওপর নির্ভর করছে!

মানে, নতুন মেসেজ এলেই:

  • messages বদলাবে
  • ইফেক্ট আবার চলবে
  • ফলে createConnection() আবার চলবে
  • ফলে পুরোনো কানেকশন কেটে নতুন কানেকশন হবে

এইভাবে বারবার কানেকশন রিফ্রেশ হওয়া কোনো ইউজারই পছন্দ করবে না। 😕


সমাধান: আপডেটার ফাংশন ব্যবহার করুন

আপনি যদি setMessages()-এ সরাসরি পুরনো স্টেট না পড়ে, বরং React-কে বলেন নিজেই আগের মান দিয়ে দাও, তাহলে আপনি সেই সমস্যা এড়াতে পারবেন।

এরকমভাবে কোড লিখুন:

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);
 
  useEffect(() => {
    const connection = createConnection();
    connection.connect();
 
    connection.on("message", (receivedMessage) => {
      setMessages((prevMessages) => [...prevMessages, receivedMessage]);
    });
 
    return () => connection.disconnect();
  }, [roomId]);
}

এখানে setMessages()-এ আমরা একটা আপডেটার ফাংশন দিয়েছি — prevMessages => [...prevMessages, receivedMessage]

এটার মানে হচ্ছে: 👉 React নিজেই আপনাকে আগের messages দিয়ে দেবে 👉 আপনি সেই আগের মেসেজের শেষে নতুনটা যোগ করবেন

এভাবে messages স্টেটকে পড়ার দরকারই পড়ে না তাই messages আর ইফেক্টের ডিপেন্ডেন্সি লিস্টে রাখার দরকার হয় না।


উপসংহার:

  • ইফেক্টের ভেতরে স্টেট পড়লে সেটা ডিপেন্ডেন্সি হয়ে যায়
  • কিন্তু যদি আপডেটার ফাংশন ব্যবহার করেন, তাহলে ইফেক্টে সেই স্টেটকে পড়তে হয় না
  • এতে ইফেক্ট অপ্রয়োজনীয়ভাবে আবার চলে না
  • এবং ইউজারের জন্য অ্যাপটা হয়ে ওঠে স্মুথ, ঝামেলাহীন

চলুন ভাই, Section 2.4 আমরা প্রাঞ্জল ও সহজ বাংলায় শুরু করি — যেন মনে হয় আপনার আপন ভাই আপনাকে বুঝিয়ে বলছে। 😌


আপনি কি কোনো ভ্যালু পড়তে চান, কিন্তু চাচ্ছেন না সেটার পরিবর্তনে ইফেক্ট রি-রান হোক?

⚠️ এই অংশে আমরা React-এর একটি এক্সপেরিমেন্টাল API নিয়ে কথা বলব — এটি এখনো React-এর স্ট্যাবল ভার্সনে রিলিজ হয়নি।


ধরুন, আপনি এমন একটা ফিচার বানাচ্ছেন — যখন নতুন মেসেজ আসবে, তখন একটা সাউন্ড বাজবে — তবে সেটা যেন বাজে না যদি ইউজার “Mute” করে রাখে।

চলুন, এর জন্য একটা সাধারণ কোড দেখি:

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);
  const [isMuted, setIsMuted] = useState(false);
 
  useEffect(() => {
    const connection = createConnection();
    connection.connect();
    connection.on("message", (receivedMessage) => {
      setMessages((msgs) => [...msgs, receivedMessage]);
      if (!isMuted) {
        playSound();
      }
    });
    return () => connection.disconnect();
  }, [roomId, isMuted]); // ✅ ডিপেনডেন্সি ঠিক মতো দেয়া হয়েছে
}

এখানে সমস্যা হচ্ছে— যখনই isMuted বদলাবে, তখন এই ইফেক্ট আবার চালু হবে (re-run), এবং নতুন করে চ্যাটে কানেক্ট হবে।

🙁 অথচ আমরা তো শুধু isMuted চেক করে সাউন্ড বাজাব কিনা, সেটা জানতেই চাচ্ছি। তার মানে, isMuted বদলালেও চ্যাট রি-কানেক্ট হোক — এটা আমরা চাই না।


তাহলে সমাধান কী?

আপনাকে সেই কোডটুকু বের করে আনতে হবে যেটা “reactive” না — মানে যার জন্য ইফেক্ট রি-রান হওয়া উচিত না। এখানে আমাদের কাজে দেবে 👉 useEffectEvent নামে এক নতুন হেল্পার।

import { useState, useEffect, useEffectEvent } from "react";
 
function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);
  const [isMuted, setIsMuted] = useState(false);
 
  const onMessage = useEffectEvent((receivedMessage) => {
    setMessages((msgs) => [...msgs, receivedMessage]);
    if (!isMuted) {
      playSound();
    }
  });
 
  useEffect(() => {
    const connection = createConnection();
    connection.connect();
    connection.on("message", (receivedMessage) => {
      onMessage(receivedMessage);
    });
    return () => connection.disconnect();
  }, [roomId]); // ✅ এখন আর isMuted এর জন্য রি-রান হয় না
}

এখানে onMessage() হচ্ছে আমাদের non-reactive logic — যেটা isMuted চেক করে, কিন্তু ইফেক্ট আবার চালু করে না।


🔄 এবার চলুন, একইরকম আরেকটা সমস্যা দেখি...

ধরুন, আপনার কম্পোনেন্টে প্যারেন্ট থেকে একটা onReceiveMessage ফাংশন আসে:

function ChatRoom({ roomId, onReceiveMessage }) {
  useEffect(() => {
    const connection = createConnection();
    connection.connect();
    connection.on("message", (receivedMessage) => {
      onReceiveMessage(receivedMessage);
    });
    return () => connection.disconnect();
  }, [roomId, onReceiveMessage]);
}

এখানে যদি প্যারেন্ট প্রতিবার নতুন নতুন ফাংশন পাঠায়, তাহলে onReceiveMessage ডিপেন্ডেন্সি বদলাবে — আর ইফেক্ট আবার চালু হবে। ফলে প্রত্যেকবার চ্যাট আবার কানেক্ট হবে!

সমাধান?

❇️ আবারও useEffectEvent ব্যবহার করুন:

function ChatRoom({ roomId, onReceiveMessage }) {
  const onMessage = useEffectEvent((receivedMessage) => {
    onReceiveMessage(receivedMessage);
  });
 
  useEffect(() => {
    const connection = createConnection();
    connection.connect();
    connection.on("message", (receivedMessage) => {
      onMessage(receivedMessage);
    });
    return () => connection.disconnect();
  }, [roomId]); // ✅ শুধু roomId বদলালে ইফেক্ট চালু হবে
}

✅ রিএক্টিভ আর নন-রিএক্টিভ কোড আলাদা করা

একটা উদাহরণ ধরুন: আপনি roomId চেঞ্জ হলে একটি লগ রাখতে চান। এই লগে আপনি বর্তমান notificationCount ও যুক্ত করতে চান, কিন্তু notificationCount বদলালে নতুন লগ না চাই।

তাহলে আপনি notificationCount পড়বেন useEffectEvent এর ভিতর থেকে:

function Chat({ roomId, notificationCount }) {
  const onVisit = useEffectEvent((visitedRoomId) => {
    logVisit(visitedRoomId, notificationCount);
  });
 
  useEffect(() => {
    onVisit(roomId);
  }, [roomId]); // ✅ শুধু roomId বদলালেই লগ হবে
}

চলুন, আমরা এখন সেকশন “কোনো রিঅ্যাক্টিভ ভ্যালু অনিচ্ছাকৃতভাবে বদলে যাচ্ছে কি?” সহজ বাংলায় লিখি, যেন আপনি নিজেই বুঝতে পারেন এবং অন্যকেও বোঝাতে পারেন।


২.৫: কোনো রিঅ্যাক্টিভ ভ্যালু কি অপ্রয়োজনীয়ভাবে বদলে যাচ্ছে?

অনেক সময় আমরা চাই, ইফেক্ট যেন কোনও ভ্যালু বদলালে রিঅ্যাক্ট করে — অর্থাৎ, নতুন কিছু হলে কাজ করে। কিন্তু সমস্যা হয় তখন, যখন সেই ভ্যালু বদলায় বারে বারে, অথচ ইউজারের দৃষ্টিতে আসলে কিছুই বদলায়নি।

চলুন একটা উদাহরণ দেখি।

ধরুন, আপনি এমন একটা অবজেক্ট তৈরি করলেন:

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

এটা আপনি কম্পোনেন্টের ভেতরে বানালেন, তাই এটা React-এর চোখে reactive value। আর আপনি যদি এটা ইফেক্টের মধ্যে ব্যবহার করেন, তাহলে আপনাকে এটা dependency হিসেবে দিতে হবে:

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

এভাবে দিলে, React বুঝবে options পরিবর্তিত হলে আবার effect চালাতে হবে। এতদূর পর্যন্ত ঠিক।


তাহলে সমস্যা কোথায়?

সমস্যাটা বোঝার জন্য নিচের জিনিসটা ভাবুন: আপনি ইনপুটে কিছু লিখছেন — মানে শুধু message স্টেট আপডেট হচ্ছে।

আপনার তো চ্যাট কানেকশন বদলানোর কথা না, তাই না?

কিন্তু কী হচ্ছে জানেন?

প্রতিবার আপনি টাইপ করছেন, পুরো ChatRoom কম্পোনেন্ট আবার রেন্ডার হচ্ছে। আর যেহেতু options অবজেক্ট কম্পোনেন্টের ভেতরে তৈরি হয়, প্রতিবারই এটা নতুন করে বানানো হচ্ছে।

React ভাবে এই নতুন options পুরোনোটার থেকে আলাদা, যদিও ভিতরের মান একই।

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

JavaScript-এ দুইটা আলাদা অবজেক্ট মানে — ওরা কখনো এক না, যতই ভিতরের মান এক হোক।


তাই হচ্ছে কী?

React ভাবে options বদলে গেছে → ইফেক্ট আবার চালায় → চ্যাট আবার কানেক্ট হয়। আর আপনি কিছু না করেও বারবার কানেকশন লগ দেখতে পান। এটা তো চাইনি!


এই সমস্যাটা মূলত হয় অবজেক্ট বা ফাংশনকে dependency হিসেবে দিলে

কারণ, তারা প্রতিবার নতুন হয়ে তৈরি হয় — React সেটা আলাদা ভেবে নেয়।


সমাধান কী?

এই সমস্যার সমাধান কয়েকভাবে করা যায়:

অবজেক্টটা কম্পোনেন্টের বাইরে নিয়ে যান (যদি সম্ভব হয়)। ✅ অবজেক্টটা ইফেক্টের ভেতরেই তৈরি করুন, যদি সেটা শুধু ইফেক্টের কাজের জন্য হয়। ✅ অবজেক্ট ভাঙা যায় কিনা দেখুন — মানে, roomId আর serverUrl আলাদা আলাদা dependency হিসেবে দিলে কেমন হয়।


অসাধারণ! চলুন, আপনার দেওয়া বিষয়টা আমরা React Bangla বইয়ের জন্য সহজ, প্রাঞ্জল, বন্ধুত্বপূর্ণ ভাষায় লিখে দিই। সেকশন ২.৫.১ — “কম্পোনেন্টের বাইরে স্ট্যাটিক অবজেক্ট ও ফাংশনগুলো সরানো” এমন কিছু করে যা React এ ভালো অভ্যাস।


২.৫.১: কম্পোনেন্টের বাইরে স্ট্যাটিক অবজেক্ট ও ফাংশনগুলো রাখুন

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

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


উদাহরণ ১: অবজেক্ট কম্পোনেন্টের বাইরে রাখা

const options = {
  serverUrl: "https://localhost:1234",
  roomId: "music",
};
 
function ChatRoom() {
  const [message, setMessage] = useState("");
 
  useEffect(() => {
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, []); // ✅ সব ডিপেন্ডেন্সি ঠিক মতো ডিক্লেয়ার করা হয়েছে
 
  // ...
}

এখানে options অবজেক্টটা কম্পোনেন্টের বাইরে রাখা হয়েছে। এটার মান কোনোভাবেই বদলাবে না, তাই React এর লিন্টার জানে এটা রিএক্টিভ নয়

ফলে, ChatRoom রি-রেন্ডার হলেও এই ইফেক্ট আবার চালু হবে না।


উদাহরণ ২: ফাংশন কম্পোনেন্টের বাইরে রাখা

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();
  }, []); // ✅ সব ডিপেন্ডেন্সি সঠিক আছে
 
  // ...
}

createOptions ফাংশনটি কম্পোনেন্টের বাইরে থাকার কারণে এটাও রিএক্টিভ নয়।

তাই ইফেক্টের ডিপেন্ডেন্সি লিস্টে createOptions দিতে হবে না।

ফলে এই ফাংশন আর ইফেক্ট রি-রানের কারণ হবে না।


ছোট টিপস:

  • যত সম্ভব স্ট্যাটিক (অর্থাৎ প্রপ বা স্টেটের ওপর নির্ভরশীল নয়) সব অবজেক্ট ও ফাংশন কম্পোনেন্টের বাইরে রাখুন।
  • এতে আপনার কোড হবে ক্লিন এবং React এর পারফরম্যান্সও বাড়বে।
  • আর লিন্টার কোনো অপ্রয়োজনীয় ওয়ার্নিং দেবে না।

অবশ্যই! আমি এখন সেকশন ২.৫.২ এর জন্য সহজ, প্রাঞ্জল এবং বাংলা ভাষায় লিখে দিচ্ছি। যেন এটা স্বাভাবিক বাংলায়, অনুবাদের মতো না হয়।


২.৫.২ ডাইনামিক অবজেক্ট আর ফাংশনগুলো ইফেক্টের ভেতরে নিয়ে আসুন

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

কেন?

কারণ ইফেক্টের বাইরে থাকলে, React মনে করবে ওই অবজেক্ট বা ফাংশনটি বদলেছে (যদিও আসলে বদলায়নি), আর বারবার ইফেক্ট রি-রান করবে। আর এর ফলে হয়তো আপনার অ্যাপ অপ্রয়োজনীয় কাজ করবে।

কোড দিয়ে বোঝানো যাক:

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]); // ✅ সব ডিপেন্ডেন্সি ঠিক মতো দেয়া আছে
  // ...
}

এখানে options অবজেক্টটা ইফেক্টের ভিতরে বানানো হয়েছে। ফলে React মনে রাখে, options নিজেই কোনো ডিপেন্ডেন্সি না। ইফেক্ট শুধুমাত্র roomId এর পরিবর্তনে রি-রান করবে।

আরেকটা গুরুত্বপূর্ণ ব্যাপার হলো, roomId এখানে একটা স্ট্রিং বা নম্বর। JavaScript এ সংখ্যা বা স্ট্রিং কনটেন্টের ভিত্তিতে চেক হয়, মানে যেই মানটা আছে সেটা বদলায় নি কিনা। এটা আরেকটু সহজে বুঝতে নিচের কোড দেখুন:

const roomId1 = "music";
const roomId2 = "music";
 
console.log(Object.is(roomId1, roomId2)); // true

roomId1 আর roomId2 একই স্ট্রিং, তাই React ঠিকমতো বুঝতে পারে কখন ইফেক্ট রি-রান করা দরকার।

এই পরিবর্তন করার ফলে, যখন আপনি ইনপুট বক্সে টাইপ করবেন, তখন চ্যাট আবার কানেক্ট করবে না — যা আমরা চাই না।


পুরো কোডের কাজের উদাহরণ:

import { useState, useEffect } from "react";
import { createConnection } from "./chat.js";
 
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]);
 
  return (
    <>
      <h1>স্বাগতম {roomId} রুমে!</h1>
      <input value={message} onChange={(e) => setMessage(e.target.value)} />
    </>
  );
}
 
export default function App() {
  const [roomId, setRoomId] = useState("general");
  return (
    <>
      <label>
        চ্যাট রুম নির্বাচন করুন:{" "}
        <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} />
    </>
  );
}

ফাংশনও একইভাবে কাজ করে

আপনি চাইলে ইফেক্টের ভিতরে ফাংশনও ডিফাইন করতে পারেন, যাতে লজিক গুলো গুছিয়ে রাখা যায়।

const serverUrl = "https://localhost:1234";
 
function ChatRoom({ roomId }) {
  const [message, setMessage] = useState("");
 
  useEffect(() => {
    function createOptions() {
      return {
        serverUrl: serverUrl,
        roomId: roomId,
      };
    }
 
    const options = createOptions();
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // ✅ সব ডিপেন্ডেন্সি ঠিক আছে
  // ...
}

এখানে createOptions() ফাংশনটা ইফেক্টের ভিতরে আছে, তাই React এর জন্য এটা কোনো রিয়েক্টিভ ডিপেন্ডেন্সি নয়। তাই এটি ডিপেন্ডেন্সি অ্যারে তে দিতে হবে না।


সংক্ষেপে: যে ডেটা বা ফাংশন ইফেক্টের ভিতরে থাকে, সেগুলো React বারবার চেক করে না। তাই ডাইনামিক, পরিবর্তনশীল অবজেক্ট আর ফাংশনগুলো ইফেক্টের বাইরে না রেখে, ইফেক্টের ভিতরে রাখাই বুদ্ধিমানের কাজ।


অবশ্যই, ভাই! চলুন, React Bangla বইয়ের স্টাইলে, সরল ও প্রাঞ্জল ভাষায় সেকশন ২.৫.৩ শুরু করি।


অবজেক্ট থেকে প্রিমিটিভ ভ্যালু পড়া

কখনো কখনো আপনি props থেকে একটা অবজেক্ট পেতে পারেন।

উদাহরণস্বরূপ:

function ChatRoom({ options }) {
  const [message, setMessage] = useState("");
 
  useEffect(() => {
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [options]); // ✅ সব ডিপেন্ডেন্সি ঠিকভাবে দেয়া হয়েছে
  // ...
}

এখানে একটা ঝুঁকি আছে — প্যারেন্ট কম্পোনেন্ট যখন রেন্ডার করবে, তখন সেটা প্রতিবার একটা নতুন অবজেক্ট তৈরি করবে:

<ChatRoom
  roomId={roomId}
  options={{
    serverUrl: serverUrl,
    roomId: roomId,
  }}
/>

এর ফলে, আপনার useEffect প্রতি রেন্ডারে আবার চালু হয়ে যাবে, অর্থাৎ বারবার রি-কানেক্ট হবে।


সমস্যা থেকে বাঁচার সহজ উপায়

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

function ChatRoom({ options }) {
  const [message, setMessage] = useState("");
 
  const { roomId, serverUrl } = options; // এখানে অবজেক্ট থেকে প্রিমিটিভ মান বের করা হলো
 
  useEffect(() => {
    const connection = createConnection({
      roomId: roomId,
      serverUrl: serverUrl,
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, serverUrl]); // ✅ স্পষ্টভাবে প্রয়োজনীয় মান গুলো দেয়া হয়েছে
  // ...
}

কেন এটা ভালো?

  • এখানে স্পষ্ট হয়েছে, ইফেক্ট আসলে roomId আর serverUrl এর উপর নির্ভর করে।
  • প্যারেন্ট যদি অবজেক্টটা বারবার নতুন করে তৈরি করলেও, যদি মানগুলো একই থাকে, ইফেক্ট আবার চালু হবে না।
  • আর যদি roomId বা serverUrl বদলায়, তখনই ইফেক্ট নতুন করে রান হবে।

কিছু কোড কিছুটা রিপিটেটিভ মনে হতে পারে — মানে অবজেক্ট থেকে ভ্যালু নিয়ে আবার নতুন অবজেক্ট তৈরি করা হচ্ছে — কিন্তু এই ছোট্ট কাজটা আপনার কোডকে অনেক বেশি সুরক্ষিত আর পরিষ্কার রাখে।


দারুণ! চলো আমরা এখন 2.5.4: ফাংশন থেকে প্রিমিটিভ ভ্যালু বের করা এই অংশটা একদম প্রাঞ্জল, স্বাভাবিক বাংলায় শুরু করি — যেন React শেখা আনন্দদায়ক হয়, অনুবাদের মতো না লাগে।


🧠 ২.৫.৪: ফাংশন থেকে সাধারণ মান (primitive value) বের করা

অনেক সময় আমরা কম্পোনেন্টে ফাংশন পাঠাই। যেমন নিচের কোডে getOptions নামে একটা ফাংশন পাঠানো হয়েছে:

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

এই ফাংশন যদি আপনি ইফেক্টের ডিপেন্ডেন্সি লিস্টে দেন, তাহলে রেন্ডার হলেই ইফেক্ট আবার চালু হবে। কারণ, প্রতিবার রেন্ডার হলেই এই ফাংশনের নতুন ভার্সন তৈরি হয়

সমাধান কী?

ফাংশনটা ইফেক্টের বাইরে কল করে ফেলুন। তাহলে আপনি শুধু সেই ফাংশন থেকে বের হওয়া সাধারণ মানগুলো (roomId, serverUrl) ব্যবহার করতে পারবেন — যেগুলো বারবার বদলায় না।

উদাহরণ দিয়ে বোঝানো যাক:

function ChatRoom({ getOptions }) {
  const [message, setMessage] = useState("");
 
  const { roomId, serverUrl } = getOptions(); // ⬅️ ইফেক্টের বাইরে কল করা হয়েছে
 
  useEffect(() => {
    const connection = createConnection({
      roomId: roomId,
      serverUrl: serverUrl,
    });
    connection.connect();
 
    return () => connection.disconnect();
  }, [roomId, serverUrl]); // ✅ এখানে শুধুই প্রিমিটিভ ডিপেন্ডেন্সি
}

এখানে getOptions() শুধু রেন্ডার টাইমে একবার কল হয়েছে। তার থেকে পাওয়া roomId আর serverUrl হচ্ছে সাধারণ (primitive) মান — যেগুলোকে ইফেক্টের ডিপেন্ডেন্সি হিসেবে রাখা নিরাপদ।


⚠️ একটা বিষয় খেয়াল রাখুন:

এই টেকনিক কেবল তখনই কাজ করে, যখন getOptions() একটা pure function — মানে, এটা শুধু ইনপুট নিয়ে রিটার্ন করে আউটপুট, আর কোনও সাইড ইফেক্ট করে না।

যদি ফাংশনটা ইভেন্ট হ্যান্ডলার টাইপের হয়, এবং আপনি চান না তার মান পরিবর্তন হলে ইফেক্ট আবার চলুক — তাহলে সেটা ইফেক্ট ইভেন্টে রূপান্তর করুন (Effect Event নিয়ে আমরা পরে আরও বিস্তারিত শিখব)।


📌 সংক্ষেপে মনে রাখুন (Recap)

  • ডিপেন্ডেন্সি সবসময় সেই কোডের সাথে মিলে যেতে হবে।
  • ডিপেন্ডেন্সি বাদ দিতে চাইলে, লিন্টারকে "প্রমাণ" দিতে হবে কেন সেটা জরুরি না।
  • ইচ্ছেমতো লিন্টারকে চুপ করালে ভবিষ্যতে জটিল বাগ হবে।
  • ইউজারের কোনো কাজের ভিত্তিতে কোড চালাতে হলে — সেটা ইভেন্ট হ্যান্ডলারে রাখুন।
  • ইফেক্টের আলাদা আলাদা অংশ যদি আলাদা কারণে চালাতে চান — ইফেক্ট ভাগ করে ফেলুন।
  • আগের স্টেট দেখে নতুন স্টেট আপডেট করতে চাইলে, আপডেটার ফাংশন ব্যবহার করুন।
  • সর্বশেষ মান পড়তে চাইলে, কিন্তু তার পরিবর্তনের রিঅ্যাক্ট করতে না চাইলে, ইফেক্ট ইভেন্ট ব্যবহার করুন।
  • জাভাস্ক্রিপ্টে নতুন করে বানানো অবজেক্ট বা ফাংশনকে পুরনোটা থেকে আলাদা হিসেবে ধরে।
  • ইফেক্টে অবজেক্ট বা ফাংশনকে ডিপেন্ডেন্সি বানানো এড়িয়ে চলুন। ওগুলো বাইরে নিয়ে আসুন বা ইফেক্টের ভিতরেই তৈরি করুন।