আপনার হয়তো useEffect-এর দরকার নেই

useEffect হচ্ছে React-এর নিয়মকানুন (paradigm) থেকে বের হয়ে বাইরের কোনো সিস্টেমের (যেমন: API, DOM, থার্ড-পার্টি লাইব্রেরি) সাথে যোগাযোগ করার দরজা। কিন্তু যদি React কম্পোনেন্টের ভিতরে থাকা props বা state পরিবর্তনের ভিত্তিতে কিছু করার দরকার হয়, তাহলে useEffect এর প্র‍য়োজন নেই। অপ্রয়োজনীয় useEffect ব্যবহার করলে কোড জটিল হয়, পারফরম্যান্স কমে যায়, এবং বাগ হওয়ার ঝুঁকি বাড়ে।

  • কখন এবং কেন অপ্রয়োজনীয় useEffect বাদ দিতে হবে
  • কিভাবে খরচসাপেক্ষ (expensive) গণনা Effect ছাড়া ক্যাশ করবেন
  • কিভাবে state reset বা আপডেট করবেন useEffect ছাড়া
  • কিভাবে একাধিক ইভেন্ট হ্যান্ডলারে লজিক শেয়ার করবেন
  • কোন লজিক ইভেন্ট হ্যান্ডলারে রাখা উচিত
  • কিভাবে প্যারেন্ট কম্পোনেন্টকে পরিবর্তনের খবর দেবেন

কিভাবে অপ্রয়োজনীয় useEffect বাদ দিবেন

React-এ দুইটি সাধারণ ক্ষেত্রে useEffect না লিখেও আপনার কাজ হয়ে যাবে:


✅ আপনি যদি ডাটা ফিল্টার/পরিবর্তন করে দেখাতে চান

ভুল পদ্ধতি (যেখানে অনেকে useEffect ব্যবহার করে):

const [filteredList, setFilteredList] = useState([]);
 
useEffect(() => {
  setFilteredList(list.filter((item) => item.active));
}, [list]);

ভাল পদ্ধতি (Effect ছাড়া):

const filteredList = list.filter((item) => item.active);

📌 কেন? React যখন রেন্ডার করে, তখন সে filteredList আবার হিসাব করে নেয়। আলাদা করে useEffect দিয়ে state সেট করলে React অতিরিক্ত রেন্ডার করে। তাই শুধু দেখানোর জন্য ফিল্টার করলে, সরাসরি উপরের মতো লিখলেই যথেষ্ট।


✅ আপনি যদি ইউজারের event হ্যান্ডল করতে চান

ভুল পদ্ধতি (Effect দিয়ে event ধরে):

useEffect(() => {
  if (hasBought) {
    fetch("/api/buy");
    alert("Thanks!");
  }
}, [hasBought]);

ভাল পদ্ধতি (ইভেন্ট হ্যান্ডলারে সরাসরি কাজ):

function handleBuyClick() {
  fetch("/api/buy");
  alert("Thanks!");
}

📌 কেন? ইউজার কখন কি করেছে সেটা আপনি ইভেন্ট হ্যান্ডলারের ভিতরে জানেন। কিন্তু useEffect তখন চালু হয় যখন কাজ শেষ, অথচ কী হয়েছে সেটা বোঝার উপায় থাকে না। তাই ইভেন্টের কাজ ইভেন্ট হ্যান্ডলারেই করা সবচেয়ে ভালো।


🧪 তাহলে কখন useEffect ব্যবহার করবো?

আপনি যদি বাইরের সিস্টেমের সাথে React state বা UI সিঙ্ক করতে চান, তখন useEffect দরকার হয়।

উদাহরণ:

useEffect(() => {
  const controller = new AbortController();
  fetch("/api/products", { signal: controller.signal })
    .then((res) => res.json())
    .then((data) => setProducts(data));
  return () => controller.abort();
}, []);

📌 এটা দরকার হয় যখন:

  • API থেকে ডেটা আনতে হয়
  • DOM ম্যানিপুলেশন করতে হয়
  • WebSocket বা localStorage ব্যবহার করতে হয়
  • কোনো থার্ড-পার্টি লাইব্রেরি React এর সাথে যুক্ত করতে হয়

🔚 শেষ কথা:

আপনি কী করতে চান?useEffect দরকার?
ডাটা সাজানো (filter/sort/map)❌ না
ইউজারের ক্লিক/ইনপুট হ্যান্ডল করা❌ না
API কল / DOM পরিবর্তন / External Widget✅ হ্যাঁ

এখন আপনি বুঝে যাবেন, সব কিছুতেই useEffect দরকার হয় না। কোড সহজ ও পারফর্মেন্ট রাখতে হলে শুধুমাত্র দরকার হলে useEffect ব্যবহার করুন।

নিচে আমি আপনার দেওয়া স্টাইল অনুসরণ করে সহজ বাংলায়, টিউটোরিয়াল আকারে অনুবাদ করে দিলাম:


props বা state-এর উপর ভিত্তি করে state আপডেট

ধরুন, আপনার একটি কম্পোনেন্ট আছে যেখানে দুটি state রয়েছে: firstName এবং lastName। আপনি এই দুইটি মিলিয়ে fullName তৈরি করতে চান। আর চাইছেন, যখনই firstName বা lastName বদলাবে, fullName নিজে থেকেই আপডেট হয়ে যাক।

তখন হয়তো আপনি এইভাবে fullName কে আলাদা state হিসেবে রেখে useEffect দিয়ে আপডেট করার কথা ভাববেন:

function Form() {
  const [firstName, setFirstName] = useState("Taylor");
  const [lastName, setLastName] = useState("Swift");
 
  // 🔴 ভুল পদ্ধতি: বাড়তি state এবং অপ্রয়োজনীয় Effect
  const [fullName, setFullName] = useState("");
  useEffect(() => {
    setFullName(firstName + " " + lastName);
  }, [firstName, lastName]);
  // ...
}

এই পদ্ধতিতে কাজ unnecessary জটিল হয়ে যায়। প্রথমে fullName পুরনো (stale) মান নিয়ে রেন্ডার হয়, তারপর আবার নতুন মান দিয়ে রেন্ডার হয় — মানে এক্সট্রা রেন্ডারিং!

এর চেয়ে সহজ উপায় হলো: fullName কে সরাসরি রেন্ডারের সময় হিসাব করা:

function Form() {
  const [firstName, setFirstName] = useState("Taylor");
  const [lastName, setLastName] = useState("Swift");
  // ✅ ভালো পদ্ধতি: সরাসরি রেন্ডারের সময় হিসাব করা
  const fullName = firstName + " " + lastName;
  // ...
}

📌 যখন কিছু আপনি সহজেই props বা state থেকে হিসাব করতে পারেন, তখন সেটাকে state-এ রাখার দরকার নেই। বরং সরাসরি রেন্ডারের সময় হিসাব করে ফেলুন।

এর ফলে আপনি পাবেন:

  • ✅ ভালো পারফরম্যান্স (কারণ বাড়তি রেন্ডার হবে না)
  • ✅ কম কোড (বাড়তি state ও useEffect বাদ)
  • ✅ কম বাগ (state গুলোর মধ্যে ভুলভাবে mismatch হওয়ার ঝুঁকি কম)

যদি এই কনসেপ্টটা নতুন লাগে, তাহলে "Thinking in React" (opens in a new tab) গাইডটি দেখলে পরিষ্কার ধারণা পাবেন কোন জিনিসগুলো state-এ রাখা উচিত আর কোনগুলো নয়।


নিচে আমি সহজ ভাষায়, টিউটোরিয়াল স্টাইলে বাংলায় অনুবাদ করলাম:


যখন কোনো prop পরিবর্তিত হয়, তখন কিছু state অ্যাডজাস্ট করা

কখনো কখনো আপনি চাচ্ছেন যে, কোনো prop পরিবর্তিত হলে state-এর কিছু অংশ রিসেট বা পরিবর্তন হোক — কিন্তু পুরোটা নয়।

ধরুন এই List কম্পোনেন্টটি items নামের একটি prop নিচ্ছে এবং selection নামে একটি state রাখছে, যেটা নির্বাচিত আইটেম ট্র্যাক করে। আপনি চাইছেন, যখনই items নতুন কোনো অ্যারে পায়, তখন selection যেন null হয়ে যায়।

আপনি হয়তো এমন কোড লিখতে পারেন:

function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
  const [selection, setSelection] = useState(null);
 
  // 🔴 ভুল: prop পরিবর্তনে Effect দিয়ে state আপডেট করা
  useEffect(() => {
    setSelection(null);
  }, [items]);
  // ...
}

❌ এই পদ্ধতিও পারফেক্ট নয়। কারণ এখানে যা হচ্ছে:

  1. items বদলানোর পর List এবং এর চাইল্ড কম্পোনেন্টগুলো পুরনো selection দিয়ে রেন্ডার হয়।
  2. তারপর React DOM আপডেট করে এবং useEffect চালায়।
  3. এরপর setSelection(null) দিয়ে আবার রেন্ডার শুরু হয়।

অর্থাৎ এক্সট্রা এক রেন্ডারিং সাইকেল হয়ে যায়।

✅ এই সমস্যার ভালো সমাধান হলো: useEffect বাদ দিয়ে রেন্ডারের সময় সরাসরি স্টেট আপডেট করা:

function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
  const [selection, setSelection] = useState(null);
 
  // ✅ ভালো: রেন্ডারের সময়ই চেক ও আপডেট
  const [prevItems, setPrevItems] = useState(items);
  if (items !== prevItems) {
    setPrevItems(items);
    setSelection(null);
  }
  // ...
}

এইভাবে আগের রেন্ডারের তথ্য সংরক্ষণ (opens in a new tab) করে স্টেট ম্যানেজ করা একটু কঠিন মনে হতে পারে, কিন্তু useEffect দিয়ে একই স্টেট আপডেট করার চেয়ে এটা ভালো।

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

⚠️ মনে রাখবেন: রেন্ডারের সময় শুধু সেই একই কম্পোনেন্টের স্টেট আপডেট করতে পারবেন। অন্য কম্পোনেন্টের স্টেট আপডেট করতে গেলে React এরর দেখাবে।

🔁 items !== prevItems এই কন্ডিশনটা ব্যবহার করা জরুরি — না হলে ইনফিনিট লুপে পড়বেন।

⛔ যেকোনো DOM ম্যানিপুলেশন বা টাইমার সেট করার মতো কাজ রেন্ডারের সময় করা যাবে না — সেগুলোর জায়গা হলো useEffect বা ইভেন্ট হ্যান্ডলার।


✅ সবচেয়ে ভালো সমাধান: স্টেট এডজাস্ট করার দরকারই নেই

যদিও এই প্যাটার্ন useEffect-এর চেয়ে ভালো, তারপরও বেশিরভাগ ক্ষেত্রে আপনাকে এটা ব্যবহার করতে হবে না। কারণ props বা state-এর উপর ভিত্তি করে স্টেট আপডেট করা মানে হচ্ছে ডেটা ফ্লো আরও জটিল করা, যেটা ডিবাগ করা কষ্টকর।

তাই প্রথমে দেখুন আপনি কি পারতেন:

উদাহরণ:

selection স্টেট রেখে এবং বারবার রিসেট না করে, বরং selectedId রেখে সরাসরি রেন্ডারের সময় selection হিসাব করা যায়:

function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
  const [selectedId, setSelectedId] = useState(null);
  // ✅ শ্রেষ্ঠ পদ্ধতি: রেন্ডারের সময় হিসাব
  const selection = items.find((item) => item.id === selectedId) ?? null;
  // ...
}

এখন আর state "reset" করার দরকার নেই। যদি selectedId-র সাথে মিল থাকা আইটেম items-এর মধ্যে থাকে, তাহলে সেটা থাকবে নির্বাচিত। আর না থাকলে, selection হবে null

🎯 এই পদ্ধতিতে selection স্বাভাবিকভাবে আপডেট হয়, এবং আপনি বাড়তি ইফেক্ট বা স্টেট এডজাস্ট করার ঝামেলা থেকে বেঁচে যান।

নিচে আপনার React টিউটোরিয়াল PDF-এর জন্য সহজ ও স্পষ্ট বাংলায় অনুবাদ দিলাম:


POST রিকোয়েস্ট পাঠানো

এই Form কম্পোনেন্ট দুই ধরনের POST রিকোয়েস্ট পাঠায়। কম্পোনেন্ট যখন চালু হয় তখন এটা একটি অ্যানালাইটিক্স ইভেন্ট পাঠায়। আর যখন আপনি ফর্ম পূরণ করে Submit বাটনে ক্লিক করবেন, তখন এটা /api/register এ POST রিকোয়েস্ট পাঠাবে:

function Form() {
  const [firstName, setFirstName] = useState("");
  const [lastName, setLastName] = useState("");
 
  // ✅ সঠিক: এই কোডটা চলবে কারণ কম্পোনেন্ট দেখানো হয়েছে
  useEffect(() => {
    post("/analytics/event", { eventName: "visit_form" });
  }, []);
 
  // 🔴 ভুল: ইভেন্ট-নির্ভর লজিক useEffect এ রাখা ঠিক না
  const [jsonToSubmit, setJsonToSubmit] = useState(null);
  useEffect(() => {
    if (jsonToSubmit !== null) {
      post("/api/register", jsonToSubmit);
    }
  }, [jsonToSubmit]);
 
  function handleSubmit(e) {
    e.preventDefault();
    setJsonToSubmit({ firstName, lastName });
  }
}

আগের উদাহরণের মতোই এখানে চিন্তা করুন:

অ্যানালাইটিক্স POST রিকোয়েস্টটি useEffect-এ থাকা উচিত। কারণ এই রিকোয়েস্টটি পাঠানোর কারণ হলো ফর্মটি ইউজারের কাছে দেখানো হয়েছে। (ডেভেলপমেন্টে এটা দুইবার চালু হতে পারে, সেটা নিয়ে এখানে (opens in a new tab) বিস্তারিত আছে।)

কিন্তু /api/register POST রিকোয়েস্টটি ফর্ম দেখানোর কারণে নয়। এটা কেবল তখনই পাঠাতে হবে যখন ব্যবহারকারী Submit বাটন চাপবে। অর্থাৎ, এটা শুধুমাত্র ওই স্পেসিফিক ইন্টারঅ্যাকশনের সময় ঘটবে।

তাই দ্বিতীয় useEffect মুছে ফেলুন এবং POST রিকোয়েস্টটি সরাসরি ইভেন্ট হ্যান্ডলারে নিয়ে যান:

function Form() {
  const [firstName, setFirstName] = useState("");
  const [lastName, setLastName] = useState("");
 
  // ✅ সঠিক: এই কোড কম্পোনেন্ট দেখানোর জন্য চলছে
  useEffect(() => {
    post("/analytics/event", { eventName: "visit_form" });
  }, []);
 
  function handleSubmit(e) {
    e.preventDefault();
    // ✅ সঠিক: ইভেন্ট-নির্ভর লজিক ইভেন্ট হ্যান্ডলারে আছে
    post("/api/register", { firstName, lastName });
  }
}

কোনো লজিক ইভেন্ট হ্যান্ডলারে রাখবেন নাকি useEffect-এ — সেটা ঠিক করার জন্য মূল প্রশ্ন হলো: এই কোডটা ব্যবহারকারীর দৃষ্টিকোণ থেকে কী কারণে চালানো দরকার?

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

অবশ্যই! আরও সহজ ও সরল ভাষায় বোঝাই:


Chains of computations (গণনার ধারা)

React এ কখনো কখনো আপনি এমন কোড লিখতে পারেন যেখানে একটার পর একটা useEffect থাকে, আর প্রত্যেকটা আগের স্টেট দেখে নিজের স্টেট আপডেট করে। এটা একটা ধারা বা “chain” হয়ে যায়।

উদাহরণ:

  • প্রথমে card বদলে।
  • তারপর goldCardCount বাড়ে।
  • তারপর round বাড়ে।
  • তারপর isGameOver হয়।

এগুলো একে একে useEffect চালিয়ে একে অন্যকে ট্রিগার করে।

সমস্যা কী?

  1. ধীরগতির কারণ: প্রতিটা setState করার পর React আবার পুরো কম্পোনেন্ট ও তার সন্তানদের রি-রেন্ডার করে। ধরুন:

    • setCard → রেন্ডার
    • setGoldCardCount → রেন্ডার
    • setRound → রেন্ডার
    • setIsGameOver → রেন্ডার

    এই রেন্ডারগুলো অনেক সময় ও শক্তি নষ্ট করে।

  2. কোড জটিল ও ভঙ্গুর হয়: যদি ভবিষ্যতে আপনি গেমের আগের খেলার অবস্থা দেখানোর মতো ফিচার করেন, তাহলে পুরানো মান দিয়ে স্টেট আপডেট করলে আবার ঐ useEffect গুলো চালু হয়ে ভুল ডেটা দেখাবে। মানে এই ধরনের কোড সহজে বদলানো যায় না, খারাপ কাজ করে।


কী করলে ভালো?

  • রান্নার সময় (render) যতটুকু হিসাব করা যায়, করে রাখুন।
  • স্টেট আপডেট সব এক জায়গায়, ইভেন্ট হ্যান্ডলারে (যখন ইউজার কাজ করে) করুন।

উদাহরণ:

function Game() {
  const [card, setCard] = useState(null);
  const [goldCardCount, setGoldCardCount] = useState(0);
  const [round, setRound] = useState(1);
 
  // রান্নার সময় হিসাব করুন
  const isGameOver = round > 5;
 
  function handlePlaceCard(nextCard) {
    if (isGameOver) {
      throw Error("Game already ended.");
    }
 
    // সব হিসাব এখানে একবারে করুন
    setCard(nextCard);
    if (nextCard.gold) {
      if (goldCardCount <= 3) {
        setGoldCardCount(goldCardCount + 1);
      } else {
        setGoldCardCount(0);
        setRound(round + 1);
        if (round === 5) {
          alert("Good game!");
        }
      }
    }
  }
}

এভাবে কোড দ্রুত চলে, কম রি-রেন্ডার হয়, এবং পরবর্তী আপডেট সহজ হয়।


ছোট্ট কিন্তু গুরুত্বপূর্ণ কথা:

  • ইভেন্ট হ্যান্ডলারের মধ্যে state আসলে পুরানো মানের একটি snapshot

  • setRound(round + 1) কল করার পরও round একই থাকে যতক্ষণ রি-রেন্ডার হয় না।

  • নতুন মান দরকার হলে নিজে একটা নতুন ভেরিয়েবল বানাতে হবে, যেমন:

    const nextRound = round + 1;

কখনও কখনও চেইন করা ঠিক থাকে:

যেমন, ফর্মে অনেক ড্রপডাউন আছে যেখানে পরের ড্রপডাউনের অপশন আগের ড্রপডাউনের সিলেকশনের উপর নির্ভর করে। এক্ষেত্রে নেটওয়ার্ক থেকে ডেটা আনতে হলে useEffect দিয়ে ধাপে ধাপে আপডেট করতে হয়।


সারমর্ম:

একের পর এক useEffect দিয়ে স্টেট পরিবর্তন করলে কোড ধীর ও জটিল হয়। স্টেট পরিবর্তন সম্ভব হলে ইভেন্ট হ্যান্ডলারে একবারেই সব করুন। এতে কোড দ্রুত ও সহজ হয়।


নিশ্চিতভাবেই! নিচে আমি সরল ভাষায়, সহজ শব্দে ব্যাখ্যা করেছি — যেন React শেখা নতুনদের জন্যও বুঝতে সুবিধা হয়। প্রতিটি পয়েন্টের সাথে সম্পূর্ণ কোডসহ উদাহরণ দিয়েছি, যাতে পাঠক বুঝতে পারেন কী হচ্ছে এবং কেন হচ্ছে।


🔰 অ্যাপ শুরু হওয়ার সময় (Initialization) যা একবারই চালানো উচিত

অনেক সময় আমাদের কিছু কাজ অ্যাপ লোড হওয়ার সময় শুধু একবারই চালানো দরকার হয়। যেমন:

  • LocalStorage থেকে ডেটা লোড করা
  • ইউজারের লগইন টোকেন চেক করা

তুমি হয়তো ভাবতে পারো, এই কাজগুলো useEffect দিয়ে করা যায়, যেমন:

import { useEffect } from "react";
 
function App() {
  // ⚠️ ভুল পদ্ধতি: এটা দুইবার চলতে পারে ডেভেলপমেন্টে
  useEffect(() => {
    loadDataFromLocalStorage();
    checkAuthToken();
  }, []);
 
  return <h1>Hello App</h1>;
}
 
function loadDataFromLocalStorage() {
  console.log("📦 LocalStorage থেকে ডেটা লোড হলো");
}
 
function checkAuthToken() {
  console.log("🔐 টোকেন চেক করা হলো");
}

⚠️ সমস্যাটা কী?

React এর Strict Mode (ডেভেলপমেন্ট মোডে) useEffect() দুইবার কল করে যেন তুমি বুঝতে পারো তোমার কোড “বারবার চলে গেলে” ঠিকভাবে কাজ করবে কিনা।

তাই, উপরের কোডে checkAuthToken() এবং loadDataFromLocalStorage() দুইবার চলবে। এতে সমস্যা হতে পারে, যেমন:

  • টোকেন এক্সপায়ার হয়ে যেতে পারে
  • ডেটা ভুল হয়ে যেতে পারে

✅ সমাধান ১: ভ্যারিয়েবল দিয়ে কন্ট্রোল করা

আমরা চাই এই কোড পুরো অ্যাপ চালু হলে একবারই চলুক, বারবার না। এজন্য একটা টপ-লেভেল ভ্যারিয়েবল দিয়ে কন্ট্রোল করা যায়:

import { useEffect } from "react";
 
// এই ভ্যারিয়েবলটা টপ-লেভেলে থাকবে
let didInit = false;
 
function App() {
  useEffect(() => {
    if (!didInit) {
      didInit = true; // একবার চালানোর পর true করে দিচ্ছি
      loadDataFromLocalStorage();
      checkAuthToken();
    }
  }, []);
 
  return <h1>✅ সঠিকভাবে একবার চালানো হয়েছে</h1>;
}
 
function loadDataFromLocalStorage() {
  console.log("📦 LocalStorage থেকে ডেটা লোড হলো");
}
 
function checkAuthToken() {
  console.log("🔐 টোকেন চেক করা হলো");
}

⛑️ এটা কিভাবে কাজ করে?

  • didInit নামে একটা ভ্যারিয়েবল বাইরে declare করা হয়েছে।
  • যখন App চলে, তখন useEffect এর ভেতর চেক করা হয় — didInit যদি false হয়, তাহলে কোড চলবে।
  • একবার চলার পর didInit = true করে দেয়া হয়, তাই আর দ্বিতীয়বার চলে না।

✅ সমাধান ২: রেন্ডার হওয়ার আগেই চালানো (module initialization)

তুমি চাইলে App কম্পোনেন্ট রেন্ডার হওয়ার আগেই — অর্থাৎ ফাইল ইম্পোর্ট হওয়ার সময় — কিছু কোড একবারই চালাতে পারো:

// চেক করে নিচ্ছি যেন শুধু ব্রাউজারে চলুক
if (typeof window !== "undefined") {
  checkAuthToken();
  loadDataFromLocalStorage();
}
 
function App() {
  return <h1>🌐 আগে থেকেই ইনিশিয়ালাইজ করা হয়েছে</h1>;
}
 
function loadDataFromLocalStorage() {
  console.log("📦 LocalStorage থেকে ডেটা লোড হলো");
}
 
function checkAuthToken() {
  console.log("🔐 টোকেন চেক করা হলো");
}

⚠️ সাবধানতা:

এই প্যাটার্ন সব জায়গায় ব্যবহার করলে সমস্যা হতে পারে। কারণ:

  • যখনই এই ফাইল import হবে, তখনই কোড চালবে—even যদি কম্পোনেন্ট রেন্ডারই না হয়!
  • এতে performance কমে যেতে পারে, বিশেষ করে বড় অ্যাপে।

তাই এই প্যাটার্ন শুধু App.js বা অ্যাপের entry point-এ ব্যবহার করো।


🧠 মনে রাখার বিষয়গুলো:

বিষয়কারণ
useEffect() দিয়ে একবার চালাতে চাইলে didInit ব্যবহার করোStrict mode-এ useEffect() দুইবার চলে
ফাইল ইম্পোর্ট হওয়ার সময় চালাতে চাইলে উপরে if (typeof window !== 'undefined') দিয়ে চালাওযাতে শুধু ব্রাউজারে চলে, সার্ভারে না
কোড সহজ ও পরিষ্কার রাখতে এই লজিক শুধু App.js বা main entry point-এ রাখোযেন অন্য কম্পোনেন্টে অপ্রয়োজনীয় execution না হয়

তুমি চাইলে আমি এই অংশটাকে সুন্দরভাবে PDF-ready ডিজাইনে তৈরি করে দিতে পারি, কিংবা React Bangla বইয়ের অধ্যায়ে যুক্ত করে দিতে পারি।

অবশ্যই, নিচে তোমার দেওয়া টেক্সটের বাংলা অনুবাদ + অতিরিক্ত ব্যাখ্যা + বাস্তব জীবনের একটি সম্পূর্ণ ফিচার ভিত্তিক উদাহরণ দেওয়া হলো যেন তুমি সহজেই বুঝতে পারো।


🔁 বিষয়: Parent Component কে State Update সম্পর্কে জানানো

🧠 ধরো তুমি একটি Toggle কম্পোনেন্ট বানাচ্ছো।

এই কম্পোনেন্টে isOn নামে একটি state আছে যা true বা false হতে পারে। অর্থাৎ টগল অন আছে না অফ আছে সেটা ধরে রাখে। এই Toggle কম্পোনেন্টে দুইভাবে টগল করা যায়:

  1. ক্লিক করে
  2. ড্র্যাগ করে

তুমি চাও যখনই এই isOn স্টেট চেঞ্জ হবে, তখন যেন প্যারেন্ট কম্পোনেন্টকে জানানো হয়। এজন্য তুমি onChange নামে একটি ফাংশন প্যারেন্ট থেকে রিসিভ করে, সেটা useEffect এর মাধ্যমে কল করছো।


❌ ভুল পদ্ধতি (যা করা উচিত না)

useEffect(() => {
  onChange(isOn);
}, [isOn, onChange]);

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

  • তুমি প্রথমে setIsOn() দিয়ে টগল কম্পোনেন্টের স্টেট আপডেট করছো
  • তারপর React DOM আপডেট করে
  • তারপর useEffect চলবে, তখন onChange() কল হবে
  • তখন প্যারেন্টের স্টেট আবার আপডেট হবে এবং নতুনভাবে রেন্ডার হবে

👉 ফলে দুইবার রেন্ডার হয়, যা পারফরম্যান্সের জন্য খারাপ এবং কোডও অপ্রয়োজনীয় জটিল হয়ে যায়।


✅ সঠিক পদ্ধতি: সবকিছু একই ইভেন্ট হ্যান্ডলারে করো

function updateToggle(nextIsOn) {
  setIsOn(nextIsOn);
  onChange(nextIsOn);
}

এভাবে setIsOn() আর onChange() একসাথে কল করো ক্লিক বা ড্র্যাগ ইভেন্টে।


✅ আরও ভালো পদ্ধতি: প্যারেন্টের কাছে পুরো স্টেট তুলে দাও (lifting state up)

function Toggle({ isOn, onChange }) {
  function handleClick() {
    onChange(!isOn);
  }
 
  function handleDragEnd(e) {
    if (isCloserToRightEdge(e)) {
      onChange(true);
    } else {
      onChange(false);
    }
  }
}

এখানে Toggle কম্পোনেন্ট নিজের কোনো স্টেট রাখে না। পুরো স্টেট প্যারেন্ট কম্পোনেন্ট মেইনটেইন করে, যাকে বলে "lifting state up". এতে টগল কম্পোনেন্টটা পুরোপুরি কন্ট্রোলড হয়ে যায়।


🔦 বাস্তব উদাহরণ: ফোনের ফ্ল্যাশলাইট অন/অফ

🔧 ফিচার লিস্ট:

  1. একটি Toggle কম্পোনেন্ট থাকবে (সুইচের মতো)
  2. ইউজার ক্লিক করলে ফ্ল্যাশলাইট চালু/বন্ধ হবে
  3. ইউজার ড্র্যাগ করে ছেড়ে দিলে, বাম/ডান দেখে ফ্ল্যাশলাইট চালু/বন্ধ হবে
  4. প্যারেন্ট কম্পোনেন্ট জানবে ফ্ল্যাশলাইট চালু আছে কিনা
  5. যদি চাও, Toggle কম্পোনেন্ট স্টেট রাখবে
  6. না চাইলে সব স্টেট প্যারেন্টেই থাকবে

✅ কোড (lifting state up approach):

// Toggle.js
function Toggle({ isOn, onChange }) {
  function isCloserToRightEdge(e) {
    return e.clientX > window.innerWidth / 2;
  }
 
  function handleClick() {
    onChange(!isOn);
  }
 
  function handleDragEnd(e) {
    if (isCloserToRightEdge(e)) {
      onChange(true);
    } else {
      onChange(false);
    }
  }
 
  return (
    <div
      draggable
      onClick={handleClick}
      onDragEnd={handleDragEnd}
      className={`w-24 h-12 flex items-center rounded-full 
        ${isOn ? "bg-green-500" : "bg-gray-400"}`}
    >
      <div
        className={`w-10 h-10 rounded-full bg-white shadow-md transition-transform duration-300 
          ${isOn ? "translate-x-6" : "translate-x-0"}`}
      />
    </div>
  );
}
// App.js (Parent component)
import { useState } from "react";
import Toggle from "./Toggle";
 
export default function App() {
  const [flashOn, setFlashOn] = useState(false);
 
  return (
    <div className="min-h-screen flex flex-col items-center justify-center gap-4">
      <Toggle isOn={flashOn} onChange={setFlashOn} />
      <p className="text-xl font-bold">
        Flashlight is {flashOn ? "ON 🔦" : "OFF 🌑"}
      </p>
    </div>
  );
}

🧾 সারাংশ:

কৌশলসুবিধা
useEffect দিয়ে onChange কল❌ ধীর, দুটি রেন্ডার, ভুল ধরতে কঠিন
একই ইভেন্টে setState এবং onChange✅ দ্রুত, একটি রেন্ডার
lifting state up (stateless child)✅ সিম্পল, সিঙ্ক সমস্যা নেই, রিইউজযোগ্য