অসাধারণ ভাবনা, মোঃ মজনু মিয়া ভাই! এখন আমরা শুরু করছি নতুন অধ্যায় — প্রাঞ্জল বাংলা ভাষায়, যেন React শেখা হয় মজা আর মন থেকে।


অধ্যায়: নিজের হুক বানিয়ে বারবার ব্যবহার করুন

(Reusing Logic with Custom Hooks)


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

React আমাদের কিছু দরকারি হুক দিয়ে দেয় — যেমন useState, useEffect, useContext। কিন্তু কখনো কখনো আমরা এমন কিছু দরকারি কাজ করতে চাই, যেটার জন্য React-এ কোনো তৈরি হুক নেই।

ধরুন:

  • আপনি ইন্টারনেট কানেকশন চলছে কি না, সেটা দেখতে চান
  • বা, আপনি চ্যাট রুমে কানেক্ট করতে চান
  • বা, কোনো ডেটা সার্ভার থেকে এনে শো করতে চান

এসব কাজের জন্য React তো আলাদা করে হুক দেয়নি। তাই এসব অবস্থায় আমাদেরকেই নিজের হুক বানিয়ে নিতে হয়

এই অধ্যায়ে আমরা দেখব — কিভাবে নিজের হুক বানিয়ে সেটা বারবার ব্যবহার করা যায়।


আপনি যা শিখবেন:

  • কাস্টম হুক (Custom Hook) মানে কী
  • কিভাবে একই লজিক বারবার ব্যবহার করা যায়
  • হুকের নাম কীভাবে রাখলে বুঝতে সুবিধা হয়
  • কখন আর কেন আপনি হুককে আলাদা করে ফেলবেন

সেকশন ১: একই কাজ বারবার করবো — তাহলে হুক বানিয়ে ফেলি

চিন্তা করুন, আপনি এমন একটা অ্যাপ বানাচ্ছেন, যেটা ইন্টারনেট কানেকশনের উপর খুব নির্ভরশীল

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

এই কাজটা করতে আমাদের দুইটা জিনিস দরকার:

  1. একটা স্টেট, যেটা বলবে ইউজার এখন অনলাইনে আছে কি না
  2. একটা ইফেক্ট, যেটা উইন্ডোর online আর offline ইভেন্টে কান দেয়, আর সেই স্টেট আপডেট করে

এভাবেই আপনার কম্পোনেন্ট নেটওয়ার্ক স্ট্যাটাসের সাথে সিঙ্ক হয়ে থাকবে।

চলুন, কোডে দেখি:

import { useState, useEffect } from "react";
 
export default function StatusBar() {
  const [isOnline, setIsOnline] = useState(true);
 
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
 
    window.addEventListener("online", handleOnline);
    window.addEventListener("offline", handleOffline);
 
    return () => {
      window.removeEventListener("online", handleOnline);
      window.removeEventListener("offline", handleOffline);
    };
  }, []);
 
  return <h1>{isOnline ? "✅ Online" : "❌ Disconnected"}</h1>;
}

এখন আপনি যদি ইচ্ছা করে নিজের ওয়াই-ফাই বন্ধ করেন, দেখবেন StatusBar-এ "❌ Disconnected" লেখা চলে আসবে।

এক কথায় — কাজ হচ্ছে ঠিকঠাক।


কিন্তু সমস্যা শুরু হয় যখন এই একই লজিক আবার দরকার হয়...

ধরুন, আপনি এখন SaveButton নামে একটা বাটন বানাচ্ছেন, যেটা নেট কানেকশন না থাকলে কাজ করবে না। নেট না থাকলে বাটনে লেখা দেখাবে: “Reconnecting...”, আর নেট থাকলে বলবে: “Save progress”.

আপনি তখন ভাবলেন, “আচ্ছা! আমি তো আগের মতোই isOnline স্টেট আর ইফেক্ট লিখে ফেলতে পারি।” সেই মতোই কোড:

import { useState, useEffect } from "react";
 
export default function SaveButton() {
  const [isOnline, setIsOnline] = useState(true);
 
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
 
    window.addEventListener("online", handleOnline);
    window.addEventListener("offline", handleOffline);
 
    return () => {
      window.removeEventListener("online", handleOnline);
      window.removeEventListener("offline", handleOffline);
    };
  }, []);
 
  function handleSaveClick() {
    console.log("✅ Progress saved");
  }
 
  return (
    <button disabled={!isOnline} onClick={handleSaveClick}>
      {isOnline ? "Save progress" : "Reconnecting..."}
    </button>
  );
}

এই বাটনও কাজ করছে ঠিকভাবে। কিন্তু খেয়াল করুন — আপনি আগের মতোই হুবহু একই কোড কপি-পেস্ট করেছেন!


এখন আসল প্রশ্ন: একই কোড দুই জায়গায় লিখবো, না কিছু একটা শর্টকাট আছে?

হ্যাঁ! শর্টকাট আছে — নাম হচ্ছে Custom Hook। আমরা এই লজিকটা একটা নিজের হুকের মধ্যে রেখে দেব। তারপর StatusBar, SaveButton — যেখানেই দরকার, শুধু হুকটা কল করলেই কাজ হবে।


বলুন ভাই, আমি পরের অংশে চলে যাব? 👉 আমরা হুক তৈরি করবো: useOnlineStatus() যেটা শুধু একটা লাইনে ইউজ করলেই ইন্টারনেট কানেকশনের তথ্য পেয়ে যাবেন।

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


সেকশন ১.১: নিজের কাস্টম হুক বানিয়ে কোড সহজ করা

ধরুন, React-এর মতো useState() আর useEffect() আছে, তেমনই একটা হুক থাকত — useOnlineStatus() নামে।

তাহলে নিচের মতো দুইটা কম্পোনেন্ট খুব সহজেই লেখা যেত, আবার একইরকম কোড বারবার লিখতেও হতো না:

function StatusBar() {
  const isOnline = useOnlineStatus();
  return <h1>{isOnline ? "✅ Online" : "❌ Disconnected"}</h1>;
}
 
function SaveButton() {
  const isOnline = useOnlineStatus();
 
  function handleSaveClick() {
    console.log("✅ Progress saved");
  }
 
  return (
    <button disabled={!isOnline} onClick={handleSaveClick}>
      {isOnline ? "Save progress" : "Reconnecting..."}
    </button>
  );
}

দেখুন, দুইটা কম্পোনেন্ট-ই useOnlineStatus() হুক ব্যবহার করছে — যেটা অনলাইন/অফলাইন চেক করে নিচ্ছে।

তবে মজার ব্যাপার হলো, React-এ এমন হুক ডিফল্টভাবে নেই। কিন্তু চিন্তার কিছু নেই — আমরা নিজেরাই বানিয়ে নিতে পারি!


কিভাবে বানাবেন আপনার useOnlineStatus() হুক?

চলুন একসাথে দেখি:

function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);
 
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
 
    // ব্রাউজারে অনলাইন/অফলাইন ইভেন্ট লিসেনার লাগানো হচ্ছে
    window.addEventListener("online", handleOnline);
    window.addEventListener("offline", handleOffline);
 
    // পরিষ্কার করার জন্য ক্লিন-আপ ফাংশন
    return () => {
      window.removeEventListener("online", handleOnline);
      window.removeEventListener("offline", handleOffline);
    };
  }, []);
 
  return isOnline;
}

এই ফাংশনের শেষে আমরা isOnline রিটার্ন করছি, যেন অন্য কম্পোনেন্ট এটা ব্যবহার করতে পারে।


এবার সম্পূর্ণ অ্যাপটাকে দেখি, যেখানে আমরা এই হুক ব্যবহার করেছি:

// src/useOnlineStatus.js
import { useState, useEffect } from "react";
 
export function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);
 
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
 
    window.addEventListener("online", handleOnline);
    window.addEventListener("offline", handleOffline);
 
    return () => {
      window.removeEventListener("online", handleOnline);
      window.removeEventListener("offline", handleOffline);
    };
  }, []);
 
  return isOnline;
}
// App.jsx
import { useOnlineStatus } from "./useOnlineStatus.js";
 
function StatusBar() {
  const isOnline = useOnlineStatus();
  return <h1>{isOnline ? "✅ Online" : "❌ Disconnected"}</h1>;
}
 
function SaveButton() {
  const isOnline = useOnlineStatus();
 
  function handleSaveClick() {
    console.log("✅ Progress saved");
  }
 
  return (
    <button disabled={!isOnline} onClick={handleSaveClick}>
      {isOnline ? "Save progress" : "Reconnecting..."}
    </button>
  );
}
 
export default function App() {
  return (
    <>
      <SaveButton />
      <StatusBar />
    </>
  );
}

তাহলে উপকারটা কী হলো?

👉 এখন দুইটা কম্পোনেন্টেই অনলাইন স্ট্যাটাস চেক করার কোড এক লাইনে হয়ে গেল: const isOnline = useOnlineStatus();

👉 আর ব্রাউজারের জটিল ইভেন্ট হ্যান্ডলিং কোড চলে গেল useOnlineStatus() হুকের ভিতরে।

এইভাবে, আপনি আপনার কম্পোনেন্টের ভেতর শুধু “কি করতে চান” সেটা লিখবেন (যেমন: "আমি চাই ইউজার যদি অনলাইনে থাকে তবেই Save বাটন কাজ করুক")। কিভাবে করব, সেই ঝামেলা হুকের ভিতরই থাকল।


আপনি যদি বলেন, আমি এই লেখার পরের অংশ নিয়ে পরবর্তী সেকশন তৈরি করে দিতে পারি — যেমন: 👉 কিভাবে বড় কম্পোনেন্টকে ছোট ছোট কাস্টম হুক দিয়ে পরিষ্কার রাখা যায় 👉 কিভাবে কোনো ইফেক্টের ভেতরের নির্দিষ্ট অংশকে “reactive” না বানিয়ে রাখা যায়

IMPORTANT ONE. MUST READ

তোমার প্রশ্নটা খুব গুরুত্বপূর্ণ — useEffect() একবারই রান হচ্ছে, তাহলে অনলাইন/অফলাইনের অবস্থা কীভাবে অটো আপডেট হচ্ছে?

চলো ধাপে ধাপে, সহজভাবে ব্যাখা করি:


🔁 useEffect() শুধু একবারই চলে – এটা ঠিক।

useEffect(() => {
  // subscribe...
  return () => {
    // unsubscribe...
  };
}, []);

এখানে [] (empty dependency array) থাকার কারণে, useEffect() শুধু প্রথমবার কম্পোনেন্ট মাউন্ট হওয়ার সময় একবার চলে। আর শেষের return() অংশ চলে তখন, যখন কম্পোনেন্ট আনমাউন্ট হয়।


✅ তাহলে জাদুটা কোথায়? window.addEventListener-এ!

তুমি যখন লিখো:

window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);

এর মানে হলো — এই কোডটা ব্রাউজারকে বলে দেয়, "যখনই ইউজার অনলাইন বা অফলাইন হবে, আমাকে জানাও!" 🔔 এটা একরকম সাবস্ক্রিপশন। ব্রাউজার যেকোনো সময় নেটওয়ার্ক স্ট্যাটাস বদলালে, সে handleOnline() বা handleOffline() ফাংশনকে নিজে থেকে কল করে।


🧠 তাহলে useEffect() একবারই চললেও...

এর ভিতরে addEventListener() কল করা হচ্ছে, আর এই ইভেন্ট লিসেনারগুলো পুরো সময় অপেক্ষা করে, কখন online বা offline ইভেন্ট ঘটে।

🟢 যখন ইন্টারনেট কানেকশন ফিরে আসে → handleOnline() চলে → setIsOnline(true) 🔴 যখন কানেকশন যায় → handleOffline() চলে → setIsOnline(false)


🏁 প্রথম useState() কেন navigator.onLine দিয়ে করা হলো?

const [isOnline, setIsOnline] = useState(navigator.onLine);

➡️ navigator.onLine হল ব্রাউজারের একটা বিল্ট-ইন প্রপার্টি, যেটা বলে দিচ্ছে এখন ইউজার অনলাইনে আছে কি না। তাই প্রথম রেন্ডারেই যদি ইউজার অফলাইনে থাকে, তাহলে আমরা সেটা ধরতে পারি।


✅ সংক্ষেপে, এইভাবে কাজ করে:

  1. useState(navigator.onLine) — প্রথমবার ইউজার অনলাইনে নাকি না, তা ধরে।
  2. useEffect()addEventListener() দিয়ে ব্রাউজারে “listening” শুরু করে।
  3. যখনই ইউজার অনলাইন/অফলাইন হয় → ব্রাউজার handleOnline বা handleOffline ফাংশন চালায়।
  4. সেই ফাংশনে setIsOnline() কল হয় → ফলে রিয়্যাক্ট আবার রেন্ডার করে।

🔚 উপসংহার

তুমি ঠিক ধরেছো, useEffect() একবারই চলে। কিন্তু সেই একবারের ভিতরেই আমরা ইভেন্ট লিসেনার সেট করে দিয়ে আসি, যেগুলো সবসময় "কানে শুনে" থাকে — যতবার নেটওয়ার্ক স্ট্যাটাস বদলাবে, ততবার setIsOnline() হবে।

এইটুকু জিনিস না বুঝলে অনেকেরই মনে প্রশ্ন আসে — “একবারই তো চলে, তাহলে পরে আপডেট হচ্ছে কিভাবে?” তোমার এই কৌতূহল সত্যিই প্রশংসনীয়। 💡


চলুন, আমরা Section ১.৩: use দিয়ে হুকের নাম শুরু করা উচিত কেন — এই সেকশনটা সহজ, প্রাঞ্জল এবং একেবারে বাংলাভাষীদের বোঝার উপযোগী করে লিখি।


সেকশন ১.৩: হুকের নাম সবসময় use দিয়ে শুরু হবে কেন?

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

React হুক যখনই লিখবেন বা ব্যবহার করবেন, তখন একটা নিয়ম মাথায় রাখবেন:

নামের আগে use বসাবেন, তারপর হুকের কাজ বোঝায় এমন একটা নাম।


দুইটা নামের নিয়ম আপনাকে অবশ্যই মানতে হবে:

  1. React কম্পোনেন্টের নাম সবসময় বড় হাতের অক্ষর দিয়ে শুরু হবে, যেমন: ChatRoom, SaveButton। কারণ React শুধু বড় হাতের নামগুলোকেই কম্পোনেন্ট হিসেবে চিনতে পারে।

  2. React হুকের নাম সবসময় use দিয়ে শুরু হবে, তারপর বড় হাতের অক্ষরে বাকিটা, যেমন: 👉 useState (React এর তৈরি হুক) 👉 useOnlineStatus (আপনার বানানো কাস্টম হুক)


এই নিয়ম মানলে একটা দারুণ সুবিধা হয়:

🔍 আপনি কম্পোনেন্টে তাকিয়েই বুঝতে পারবেন, কোন কোন ফাংশনের মধ্যে স্টেট, ইফেক্ট বা React-এর ফিচার লুকিয়ে আছে।

👉 যেমন ধরুন getColor() নামে একটা ফাংশন — আপনি জানেন, এটা কোনো হুক না। মানে এটা React এর কোনো স্পেশাল নিয়মে চলে না। কিন্তু useOnlineStatus() দেখলেই বোঝা যাবে, এটা একটা হুক — এর মধ্যে হয়তো useState, useEffect বা useContext ব্যবহার করা হয়েছে।


🛠️ টিপস: আপনার কোড যদি লিন্টার দিয়ে চেক করা থাকে...

আপনার যদি React-friendly কোড এডিটর সেটআপ থাকে, তাহলে লিন্টার (যেটা কোডে ভুল ধরতে সাহায্য করে) এই নামের নিয়ম enforce করবে।

উদাহরণ: আপনি যদি useOnlineStatus হুকের নাম বদলে getOnlineStatus রাখেন, তাহলে লিন্টার আর useState বা useEffect চালাতে দেবে না! কারণ শুধু হুক ও কম্পোনেন্ট-ই অন্য হুক কল করতে পারে।


🧠 একটু গভীরে যাই: সব ফাংশনের নাম কি use দিয়ে শুরু হওয়া উচিত?

না, মোটেও না।

যদি কোনো ফাংশন হুক না ব্যবহার করে, তাহলে সেটার নামের আগে use বসানো উচিত না।

উদাহরণ দেখুন 👇

❌ ভুল:

function useSorted(items) {
  return items.slice().sort();
}

এই ফাংশনে কোনো হুক নেই। তাহলে use কেন?

✅ ঠিক:

function getSorted(items) {
  return items.slice().sort();
}

এখন এটা যে কোনো জায়গায়, এমনকি শর্ত অনুযায়ীও (if এর ভিতর) চালানো যাবে, কোনো React রুল ভাঙবে না।


তাহলে কখন use দিয়ে শুরু করব?

যখন আপনার ফাংশনের ভিতরে অন্য হুক থাকে, তখন অবশ্যই use দিয়ে শুরু করবেন।

✅ উদাহরণ:

function useAuth() {
  return useContext(Auth);
}

এটা একদম পারফেক্ট। কারণ এটি React হুক কল করছে।


⚠️ যদি এখন কোনো হুক না থাকে, কিন্তু ভবিষ্যতে রাখার পরিকল্পনা থাকে?

তাহলে শুরু থেকেই use দিয়ে শুরু করাই ভালো।

function useAuth() {
  // এখন শুধু ডেমো ইউজার ফেরত দিচ্ছে
  return TEST_USER;
 
  // পরে এখানে আসল হুক আসবে:
  // return useContext(Auth);
}

এই নিয়মে React ভবিষ্যতের জন্য প্রস্তুত থাকবে।


সংক্ষেপে বললে:

  • use দিয়ে নাম শুরু করুন শুধু তখনই, যখন ফাংশনটা সত্যিই হুক।
  • হুক না হলে, সাধারণ ফাংশনের মতো রাখুন — get, fetch, calculate — যা দরকার।

অসাধারণ, মোঃ মজনু মিয়া ভাই! চলুন আমরা আমাদের React Bangla বইয়ের সেকশন ১.৩ লিখে ফেলি — সহজ, প্রাঞ্জল, আর গল্পের মতো ভাষায় যেন সবাই বুঝতে পারে।


সেকশন ১.৩: কাস্টম হুক আপনাকে স্টেট শেয়ার করতে দেয় না, দেয় শুধু লজিক

React-এ আমরা প্রায়ই দেখি একই রকম স্টেট আর একই রকম হ্যান্ডলার বারবার লিখতে হচ্ছে। তখন মনে হয়, “একই কোড আবার আবার লিখছি কেন?” — এই সমস্যার সমাধান হলো Custom Hook

কিন্তু একটা ভুল ধারনা অনেকেই করে বসে — “আমি তো কাস্টম হুক বানালাম, এখন হয়তো সব জায়গায় একই স্টেট শেয়ার হচ্ছে!”

🛑 না ভাই! কাস্টম হুক লজিক শেয়ার করে, কিন্তু স্টেট না। চলুন, উদাহরণ দিয়ে বুঝে নিই।


আগে যা ছিল: একই লজিক বারবার লিখেছি

function StatusBar() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    // নেটওয়ার্ক স্ট্যাটাস চেক
  }, []);
}
 
function SaveButton() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    // নেটওয়ার্ক স্ট্যাটাস চেক
  }, []);
}

দেখুন, দুই জায়গায় একই জিনিস লিখেছি — দুইটা আলাদা স্টেট, আলাদা ইফেক্ট।


পরে যা করলাম: একই লজিক তুলে নিলাম কাস্টম হুকে

function StatusBar() {
  const isOnline = useOnlineStatus();
  // ...
}
 
function SaveButton() {
  const isOnline = useOnlineStatus();
  // ...
}

এখন কোড কমেছে, পরিষ্কার দেখাচ্ছে। কিন্তু ভুলে যাবেন না — 👉 StatusBar আর SaveButton একেকবার useOnlineStatus() কল করছে। 👉 প্রতিবার কল মানেই নতুন একটা স্টেট তৈরি হচ্ছে।

তাই এই দুইটা স্টেট একসাথে কানেক্টেড না। শুধু একি রকম লজিক ফলো করছে।


চলুন এবার একটা ফর্ম উদাহরণ দেখি

export default function Form() {
  const [firstName, setFirstName] = useState("Mary");
  const [lastName, setLastName] = useState("Poppins");
 
  function handleFirstNameChange(e) {
    setFirstName(e.target.value);
  }
 
  function handleLastNameChange(e) {
    setLastName(e.target.value);
  }
 
  return (
    <>
      <label>
        First name:
        <input value={firstName} onChange={handleFirstNameChange} />
      </label>
      <label>
        Last name:
        <input value={lastName} onChange={handleLastNameChange} />
      </label>
      <p>
        <b>
          Good morning, {firstName} {lastName}.
        </b>
      </p>
    </>
  );
}

এই ফর্মে দুইটা ইনপুট আছে, আর দুইবার স্টেট হ্যান্ডলিং করেছি। এখন এই পুনরাবৃত্তি কমাতে, আমরা একটা কাস্টম হুক লিখবো।


নতুন কাস্টম হুক: useFormInput()

// useFormInput.js
export function useFormInput(initialValue) {
  const [value, setValue] = useState(initialValue);
 
  function handleChange(e) {
    setValue(e.target.value);
  }
 
  return {
    value,
    onChange: handleChange,
  };
}

এবার Form কম্পোনেন্টে ব্যবহার করি:

import { useFormInput } from "./useFormInput.js";
 
export default function Form() {
  const firstNameProps = useFormInput("Mary");
  const lastNameProps = useFormInput("Poppins");
 
  return (
    <>
      <label>
        First name:
        <input {...firstNameProps} />
      </label>
      <label>
        Last name:
        <input {...lastNameProps} />
      </label>
      <p>
        <b>
          Good morning, {firstNameProps.value} {lastNameProps.value}.
        </b>
      </p>
    </>
  );
}

এখানে কিন্তু useFormInput() দুইবার কল করা হয়েছে। 👉 তাই এটা দুইটা আলাদা স্টেট তৈরি করেছে। 👉 স্টেট শেয়ার হয় না, শুধু লজিক শেয়ার হয়


শেষ কথা

🔁 কাস্টম হুক মানে বারবার লেখা কোড এক জায়গায় নিয়ে আসা।

❤️ কিন্তু ভুলে যেও না, প্রতিবার ব্যবহার করলে, প্রতিবার নতুন স্টেট তৈরি হয়।

যদি সত্যি সত্যি একাধিক কম্পোনেন্টে একই স্টেট শেয়ার করতে চাও — 👉 তাহলে সেই স্টেটকে উপরে তুলে আনো (lift up) 👉 আর নিচে props দিয়ে পাঠাও


নিচে সেকশন ২-এর প্রথম ভাগের প্রাঞ্জল ও প্রাঙ্কৃত বাংলা রূপ দেওয়া হলো—যাতে অনুবাদের ছাপ না থাকে বরং পাঠে সহজবোধ্যতা ও সাবলীলতা থাকে। পুরো সেকশনটি বড়, তাই এটিকে দুইভাগে ভাগ করে দিবো। নিচে প্রথম অংশ:


সেকশন ২: রিয়েক্টিভ মান এক হুক থেকে আরেক হুকে পাঠানো

তোমার বানানো কাস্টম হুকগুলো—একটি কম্পোনেন্ট যতবার রেন্ডার হয়, ঠিক ততবারই পুনরায় চালু হয় (re-run)। তাই এগুলোকেও ঠিক কম্পোনেন্টের মতোই pure রাখতে হয়। অর্থাৎ, বাইরের জগতের কোনো প্রভাব যেন না থাকে—একই ইনপুটে সবসময় একই আউটপুট পাওয়া যায়। সহজভাবে বললে, কাস্টম হুকের কোডটাও কম্পোনেন্টের বডির অংশ হিসেবেই ভাবতে পারো।

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

নিচের উদাহরণে প্রথমে আমরা এই লজিক সরাসরি ChatRoom কম্পোনেন্টের ভিতরেই লিখছি:

// App.js
import { useState } from "react";
import ChatRoom from "./ChatRoom.js";
 
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} />
    </>
  );
}
// ChatRoom.js
import { useState, useEffect } from "react";
import { createConnection } from "./chat.js";
import { showNotification } from "./notifications.js";
 
export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState("https://localhost:1234");
 
  useEffect(() => {
    const options = { serverUrl, roomId };
    const connection = createConnection(options);
    connection.on("message", (msg) => {
      showNotification("New message: " + msg);
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, serverUrl]);
 
  return (
    <>
      <label>
        Server URL:
        <input
          value={serverUrl}
          onChange={(e) => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
    </>
  );
}

এই উদাহরণে, যখনই roomId বা serverUrl পরিবর্তন হয়, তখনই useEffect নতুন করে চলে এবং নতুন করে চ্যাট সার্ভারে সংযোগ (connect) করে। তুমি কনসোলে লগ দেখলেই এটা বুঝতে পারবে।

এখন এই useEffect-এর লজিকটুকু সরিয়ে আমরা একটি কাস্টম হুকের ভিতরে নিয়ে যাই:

// useChatRoom.js
import { useEffect } from "react";
import { createConnection } from "./chat.js";
import { showNotification } from "./notifications.js";
 
export function useChatRoom({ serverUrl, roomId }) {
  useEffect(() => {
    const options = { serverUrl, roomId };
    const connection = createConnection(options);
    connection.connect();
    connection.on("message", (msg) => {
      showNotification("New message: " + msg);
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl]);
}

এবার ChatRoom কম্পোনেন্টে হুকটি ব্যবহার করি:

// ChatRoom.js
import { useState } from "react";
import { useChatRoom } from "./useChatRoom.js";
 
export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState("https://localhost:1234");
 
  useChatRoom({ roomId, serverUrl });
 
  return (
    <>
      <label>
        Server URL:
        <input
          value={serverUrl}
          onChange={(e) => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
    </>
  );
}

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


ধন্যবাদ মনে করিয়ে দেওয়ার জন্য, Md. Mojnu Miah ভাই। এখন আমরা Section 2: Passing reactive values between Hooks এর ২য় অংশ বাংলায় সহজ ও প্রাঞ্জল ভাষায় শেষ করে ফেলি।

সেকশন ২: হুকের মধ্যে রিএ্যাকটিভ ভ্যালু পাঠানো (চলমান...)

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

তোমার ChatRoom কম্পোনেন্টে যখনই roomId বা serverUrl বদলায়, তখন পুরো কম্পোনেন্ট আবার রেন্ডার হয়। আর যেহেতু useChatRoom হুকটাও কম্পোনেন্টের ভিতরেই কল করা, তাই সেটাও আবার চালু হয়—নতুন ভ্যালুগুলোর উপর ভিত্তি করে।

এই নতুন ভ্যালুগুলো হুকের মধ্যে গেলে useEffect দেখে নেয়, এগুলোর কোনোটা আগে থেকে আলাদা কি না। যদি ভিন্ন হয়, তবে পুরনো connection ডিসকানেক্ট করে নতুনভাবে connect() করে। কনসোলে তুমি দেখবে:

✅ Connecting to "travel" room at https://localhost:1234...

অথবা,

❌ Disconnected from "general" room at https://localhost:1234

একটা হুকের আউটপুট আরেক হুকের ইনপুট হতে পারে

useState() হুক দিয়ে তুমি serverUrl বানিয়েছো:

const [serverUrl, setServerUrl] = useState("https://localhost:1234");

তারপর এই serverUrl টা তুমি useChatRoom() হুকে দিচ্ছো:

useChatRoom({
  roomId: roomId,
  serverUrl: serverUrl,
});

এখানে useState() এর আউটপুট মানে হচ্ছে serverUrl এবং সেটা আবার useChatRoom() এর ইনপুট হিসেবে কাজ করছে। এই প্রসেসকে অনেকটা অডিও বা ভিডিও এডিটিং সফটওয়্যারের মতো ভাবতে পারো—যেখানে একটা এফেক্টের আউটপুট আরেকটা এফেক্টে ঢুকে যায়। একইভাবে useState() এর রিএ্যাকটিভ ভ্যালু ঢুকে যাচ্ছে useChatRoom() হুকে।

সংক্ষেপে মূল পয়েন্ট

✅ কম্পোনেন্টে যখন হুক ব্যবহার করো, তখন প্রতিবার রেন্ডারেই হুক আবার চলে। ✅ হুকের মধ্যে যদি useEffect() থাকে, তাহলে সেটা তার dependency (যেমন roomId, serverUrl) পরিবর্তিত হলে পুনরায় চালু হয়। ✅ তুমি চাইলে এক হুকের আউটপুটকে আরেক হুকের ইনপুট হিসেবে ব্যবহার করতে পারো। ✅ এইভাবেই আমরা হুকের মাধ্যমে রিএ্যাকটিভ ভ্যালু সহজে শেয়ার করতে পারি।


অসাধারণ! এবার শুরু করছি আমাদের নতুন সেকশন — সেকশন ২.১: কাস্টম হুক-এ ইভেন্ট হ্যান্ডলার পাঠানো আমরা আগের মতোই প্রাঞ্জল, সহজ, নিজের ভাষায় লিখব যেন এটা অনুবাদ না মনে হয়।


সেকশন ২.১: কাস্টম হুক-এ ইভেন্ট হ্যান্ডলার পাঠানো

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

এটা করার জন্য আমাদের হুক-এ একটা ইভেন্ট হ্যান্ডলার পাঠাতে হবে।

চলো প্রথমে দেখি আগের কোডটা কেমন ছিল

export function useChatRoom({ serverUrl, roomId }) {
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId,
    };
    const connection = createConnection(options);
    connection.connect();
    connection.on("message", (msg) => {
      showNotification("New message: " + msg);
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl]);
}

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


তাহলে হুক-এর ব্যবহারে কিছু পরিবর্তন আনতে হবে

export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState("https://localhost:1234");
 
  useChatRoom({
    roomId: roomId,
    serverUrl: serverUrl,
    onReceiveMessage(msg) {
      showNotification("New message: " + msg);
    },
  });
}

এখানে আমরা useChatRoom-এ একটা onReceiveMessage হ্যান্ডলার পাঠালাম। এখন হুকটা এই ফাংশনটা কল করবে যখনই নতুন মেসেজ আসবে।


এবার হুকটা একটু আপডেট করি

export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId,
    };
    const connection = createConnection(options);
    connection.connect();
    connection.on("message", (msg) => {
      onReceiveMessage(msg);
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl, onReceiveMessage]); // সব dependency ঠিকঠাক
}

এইভাবে কাজ করবে ঠিকই, কিন্তু একটা সমস্যা থেকে যাচ্ছে — onReceiveMessage যদি প্রতিবার রেন্ডারে নতুন হয়, তাহলে useEffect আবার চালু হবে, আর চ্যাট রি-কানেক্ট হবে। এটা আমরা চাই না।


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

React-এর একটা experimental API আছে — useEffectEvent। এটা দিয়ে আমরা ইভেন্ট হ্যান্ডলারকে Effect-এর বাইরের dependency বানাতে পারি।

import { useEffect, useEffectEvent } from "react";
 
export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {
  const onMessage = useEffectEvent(onReceiveMessage);
 
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId,
    };
    const connection = createConnection(options);
    connection.connect();
    connection.on("message", (msg) => {
      onMessage(msg); // এখনো ঠিকভাবে কাজ করে
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl]); // ✅ onReceiveMessage আর dependency-তে নেই
}

এখন, ChatRoom রেন্ডার যতবারই হোক না কেন, চ্যাট রুম রি-কানেক্ট হবে না — কারণ onReceiveMessage dependency-তে আর নেই।


এই সুবিধা কী?

  • এখন আপনি সহজেই এই হুকটা অন্য জায়গায় ব্যবহার করতে পারেন
  • মেসেজ এলে কী হবে সেটা নিজে ঠিক করতে পারেন
  • চ্যাট রুম অপ্রয়োজনীয়ভাবে রি-কানেক্ট হয় না

সারসংক্ষেপ:

বিষয়ব্যাখ্যা
কাস্টম হুকuseChatRoom যা ইভেন্ট হ্যান্ডলার নেয়
ইভেন্ট হ্যান্ডলারonReceiveMessage – চ্যাট মেসেজ এলে কী হবে তা ঠিক করে
সমস্যারেন্ডার হলে হুক আবার চলে, কারণ ফাংশন নতুন হয়
সমাধানuseEffectEvent দিয়ে হ্যান্ডলার রেফারেন্স ঠিক রাখা যায়

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


সেকশন ২.১: কখন কাস্টম হুক ব্যবহার করব?

React-এ কোড লিখতে গেলে অনেক সময় একই রকম কোড বারবার লিখতে হয়। প্রথমেই মনে রাখো — সবকিছুই কাস্টম হুকে ভাঙার দরকার নেই।

একই useState() দুই জায়গায় লিখে ফেললে ক্ষতি নেই। এতে কোড জটিল হয় না।

তবে যেই মুহূর্তে তুমি useEffect() ব্যবহার করো, তখন একটু ভাবতে হবে।

👉 আমরা useEffect() ব্যবহার করি যখন React-এর বাইরের কোনো জিনিসের সঙ্গে "সিঙ্ক" করতে হয় — যেমন: API থেকে ডেটা আনা, লোকাল স্টোরেজে কিছু রাখা, টাইমার সেট করা ইত্যাদি।

এমন সময় যদি আমরা useEffect() এর কাজটা একটা কাস্টম হুকে রেখে দিই, তাহলে পুরো লজিকটা পরিষ্কার হয়ে যায়।


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

ধরো তুমি একটা ShippingForm বানাচ্ছো। এই ফর্মে দুইটা ড্রপডাউন থাকবে:

  • প্রথমটা — শহরের নাম (যেটা নির্ভর করবে দেশের ওপর)
  • দ্বিতীয়টা — এলাকার নাম (যেটা নির্ভর করবে শহরের ওপর)

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

function ShippingForm({ country }) {
  const [cities, setCities] = 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]);
 
  const [city, setCity] = useState(null);
  const [areas, setAreas] = useState(null);
 
  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]);
 
  // ...
}

এখানে তুমি লক্ষ্য করলে দেখবে, দুই জায়গায় একই ধরণের কাজ হচ্ছে — API কল করে ডেটা আনা আর সেট করা।

দুইটা আলাদা useEffect() ঠিকই আছে, কারণ তারা আলাদা কাজ করছে। তবে তাদের ভেতরের লজিক প্রায় একরকম। এটাই আমরা কাস্টম হুকে তুলে আনতে পারি।


এবার দেখি কাস্টম হুক দিয়ে কীভাবে কোডটা সহজ হয়:

function useData(url) {
  const [data, setData] = useState(null);
 
  useEffect(() => {
    if (url) {
      let ignore = false;
      fetch(url)
        .then((res) => res.json())
        .then((json) => {
          if (!ignore) {
            setData(json);
          }
        });
      return () => {
        ignore = true;
      };
    }
  }, [url]);
 
  return data;
}

এবার মূল ফর্মে ব্যবহার করো:

function ShippingForm({ country }) {
  const cities = useData(`/api/cities?country=${country}`);
  const [city, setCity] = useState(null);
  const areas = useData(city ? `/api/areas?city=${city}` : null);
  // ...
}

কী লাভ হলো?

  • কোড ছোট হলো ✅
  • বোঝা সহজ হলো ✅
  • ডেটা কোথা থেকে আসে, কোথায় যায় — সেটা একদম স্পষ্ট ✅
  • ভবিষ্যতে কেউ এসে ShippingForm-এর ভিতর ঘাটাঘাটি করলেও, ভুল করে useEffect()-এ বাড়তি dependency যোগ করবে না ✅

সময় গেলে গেলে তুমি দেখবে, তোমার প্রায় সব useEffect()-ই কাস্টম হুকের ভেতরে চলে গেছে।


চলুন ভাই, আমরা সেকশন ২.৩ শুরু করি একদম আগের ধাঁচে—সহজ, প্রাঞ্জল ও বাস্তবমুখী বাংলায়। যেন React শেখা হয় নিজের ভাষায়, নিজের মতো করে।


সেকশন ২.৩: কাস্টম হুক বানান কাজের ভিত্তিতে, জীবনের মতো স্পষ্ট করে

📌 [keep-your-custom-hooks-focused-on-concrete-high-level-use-cases]

React এ আমরা অনেক সময় এমন হুক বানাই যা শুধু আমাদের নির্দিষ্ট কাজে লাগে — যেমন ডাটা আনা, স্ক্রল দেখা, কিংবা চ্যাট রুমে কানেক্ট হওয়া। এইরকম হুককে বলে কাস্টম হুক

কিন্তু কাস্টম হুক বানাতে গিয়ে অনেকেই একটা ভুল করেন — হুক বানায় খুব অ্যাবস্ট্রাক্ট বা অস্পষ্ট কাজের জন্য। এতে কোড যেমন বুঝতে কষ্ট হয়, তেমনি bug-ও হয় বেশি।


অসাধারণ, এখন আমরা শুরু করছি —

সেকশন ২.৩: Custom Hook তৈরি করুন নির্দিষ্ট ও বাস্তব কাজের জন্য

(প্রাঞ্জল, ক্লিয়ার, আরামদায়ক বাংলায়)


React-এ আমরা অনেক সময় কিছু নির্দিষ্ট কাজ বারবার করি— যেমন: সার্ভারে কানেক্ট হওয়া, লগ পাঠানো, বা স্ক্রিন সাইজ দেখা।

এইসব কাজ বারবার না লিখে একবারেই সুন্দরভাবে লিখে রাখতে চাইলে আমরা Custom Hook ব্যবহার করি। কিন্তু শুধু Hook বানালেই হবে না— সেই Hook-এর উদ্দেশ্য, নামকরণ, আর ব্যবহার যেন স্পষ্ট হয়, সেটাও নিশ্চিত করতে হয়।


🎯 প্রথম কাজ: নাম ঠিক করুন

Custom Hook বানাতে গেলে আগে চিন্তা করুন, এই Hook টা আসলে কী কাজ করছে?

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

একটা ভালো Hook-এর নাম শুনেই যেন বোঝা যায়, এটা কী করে, কী ইনপুট নেয়, আর কী রিটার্ন করে।

✅ ভালো নামের কিছু উদাহরণ:

useData(url);
useImpressionLog(eventName, extraData);
useChatRoom(options);

এগুলোর নাম দেখলেই বোঝা যায়, এগুলো কী কাজ করছে। এমনকি যিনি কোডার নন, তিনিও একটা আন্দাজ করতে পারবেন।


🛠️ টেকনিক্যাল কাজের জন্য নাম একটু আলাদা হতে পারে

যদি আপনি এমন সিস্টেমের সঙ্গে কাজ করেন যেটা একটু টেকনিক্যাল (যেমন: socket, media query ইত্যাদি), তাহলে সেই বিষয়ে অভিজ্ঞ কেউ যেন সহজেই বুঝতে পারে—এমন নাম দিতে পারেন।

✅ যেমন:

useMediaQuery(query);
useSocket(url);
useIntersectionObserver(ref, options);

এই নামগুলো স্পষ্ট, এবং যে এগুলো বোঝে, সে বুঝবে এগুলোর কী কাজ।


❌ খারাপ অভ্যাস: “লাইফসাইকেল” হুক বানানো

অনেকেই চায় useEffect কে শর্টকার্টে ব্যবহার করতে। যেমন:

useMount(fn);
useEffectOnce(fn);
useUpdateEffect(fn);

এই ধরনের হুক বানানো ভালো অভ্যাস না। কারণ এতে আপনার কোড অনেক বেশি ভেতরে ভেতরে জট পাকাতে শুরু করে।

চলুন, দেখি useMount নামে একটা হুক কীভাবে React-এর মূল নিয়ম ভাঙে:

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState("https://localhost:1234");
 
  // 🔴 সমস্যা: useMount ইউজ করলে React ঠিকভাবে ট্র্যাক করতে পারবে না
  useMount(() => {
    const connection = createConnection({ roomId, serverUrl });
    connection.connect();
    post("/analytics/event", { eventName: "visit_chat" });
  });
}
 
// 🔴 সমস্যা: এটা শুধু একবার চালায়, কিন্তু roomId বা serverUrl বদলালে চালায় না!
function useMount(fn) {
  useEffect(() => {
    fn();
  }, []);
}

এই useMount হুক কাজ করছে ঠিকই, কিন্তু এর ভেতরে যদি roomId বা serverUrl বদলে যায়— সেটা বুঝে রিঅ্যাক্ট করবে না। React এর লিন্টারও কিছু বলবে না, কারণ এটা সরাসরি useEffect না।


✅ ঠিকভাবে করা হলে কেমন হয়?

React এর নিজস্ব API ব্যবহার করুন, পরিষ্কারভাবে। Example:

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState("https://localhost:1234");
 
  // ✅ চ্যাট রুমে কানেক্ট হওয়া
  useEffect(() => {
    const connection = createConnection({ roomId, serverUrl });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, serverUrl]);
 
  // ✅ অ্যানালিটিক্সে লগ পাঠানো
  useEffect(() => {
    post("/analytics/event", { eventName: "visit_chat", roomId });
  }, [roomId]);
}

এখানে দুইটা কাজ দুইটা useEffect-এ ভাগ করা হয়েছে— যার যার উদ্দেশ্য স্পষ্ট।


এরপর চাইলে Custom Hook এ আলাদা করে নিতে পারেন:

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState("https://localhost:1234");
 
  // ✅ আলাদা হুক, যার নামেই বোঝা যায় কাজটা কী
  useChatRoom({ serverUrl, roomId });
  useImpressionLog("visit_chat", { roomId });
}

এইভাবে করলে আপনার মূল কম্পোনেন্ট পরিষ্কার থাকে, আর প্রতিটা কাজ আলাদা Hook এর মাধ্যমে সুন্দরভাবে হ্যান্ডেল হয়।


📌 শেষ কথা

একটা ভালো Custom Hook— ✅ নির্দিষ্ট একটা কাজই করে ✅ খুব বেশি অ্যাবস্ট্রাক্ট না ✅ নাম থেকেই বোঝা যায় কী কাজ করে ✅ বাইরের সিস্টেমের সাথে মেলানো হলে সেই সিস্টেমের পরিচিতদের জন্য বোধগম্য হয়

আপনার হুক যদি “সবকিছুতেই ব্যবহারযোগ্য” হয়, তাহলে সেটা হয়তো কোনো কিছুর জন্যই উপযুক্ত না


অসাধারণ! এবার আমরা Section 2.4 লিখছি — "Custom Hooks help you migrate to better patterns" — বাংলায় প্রাঞ্জলভাবে, যেন অনুবাদ না লাগে, যেন বন্ধুর মুখে শোনা কোনো সহজ ব্যাখ্যা।


সেকশন ২.৪: কাস্টম হুকস কেন কাজে লাগে, আর কীভাবে ভালো প্যাটার্নে যেতে সাহায্য করে

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

❗ কিন্তু, React টিম চায় আপনি যতটা সম্ভব কম ইফেক্ট ব্যবহার করুন। কারণ ইফেক্ট জিনিসটা একটু কম নিয়ন্ত্রিত — এতে অনেক ভুল হওয়ার ঝুঁকি থাকে।

তাই React টিম ধীরে ধীরে এমন সব ফিচার যোগ করছে, যাতে ইফেক্ট ছাড়া আপনার কাজ হয়ে যায়।

এখন কথা হলো — আজকে যেটা আপনি useEffect দিয়ে করছেন, ভবিষ্যতে যদি সেই কাজের জন্য React আলাদা কোনো ভালো সাপোর্ট দেয়, তখন আপনাকে যদি ১০টা জায়গায় গিয়ে কোড বদলাতে হয় — কেমন লাগবে?

এই ঝামেলা এড়াতেই কাস্টম হুকস কাজে লাগে।


কাস্টম হুক মানে?

আমরা ইফেক্ট বা অন্য জটিল লজিককে একটা ফাংশনের ভিতর ঢুকিয়ে ফেলি, যার নাম শুরু হয় use দিয়ে।

যেমন — আমরা একটা কাস্টম হুক বানালাম useOnlineStatus() নামে। এই হুক বলবে ইউজার এখন অনলাইনে আছে কি না।

চলুন দেখি কীভাবে:


ধাপ ১: কাস্টম হুক দিয়ে ব্যবহার

import { useOnlineStatus } from "./useOnlineStatus.js";
 
function StatusBar() {
  const isOnline = useOnlineStatus();
  return <h1>{isOnline ? "✅ অনলাইনে" : "❌ কানেকশন নেই"}</h1>;
}
 
function SaveButton() {
  const isOnline = useOnlineStatus();
 
  function handleSaveClick() {
    console.log("✅ প্রগ্রেস সেভ হয়েছে");
  }
 
  return (
    <button disabled={!isOnline} onClick={handleSaveClick}>
      {isOnline ? "সেভ করুন" : "রিইকানেক্ট হচ্ছে..."}
    </button>
  );
}

উপরে আমরা শুধু useOnlineStatus() ব্যবহার করেছি, কিন্তু এর ভিতরে কী আছে সেটা আমাদের জানার দরকার হয়নি। এই জিনিসটাই ভবিষ্যতের জন্য খুব উপকারী।


ধাপ ২: কাস্টম হুকের ভিতরে আগের ইফেক্ট কোড

import { useState, useEffect } from "react";
 
export function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);
 
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
 
    window.addEventListener("online", handleOnline);
    window.addEventListener("offline", handleOffline);
 
    return () => {
      window.removeEventListener("online", handleOnline);
      window.removeEventListener("offline", handleOffline);
    };
  }, []);
 
  return isOnline;
}

এই হুক কাজ করছে ঠিকঠাক — কিন্তু এটা নিখুঁত না। যেমন — আমরা ধরেই নিচ্ছি ইউজার শুরুতেই অনলাইনে আছে, কিন্তু হয়তো অফলাইনে ছিল।

ভবিষ্যতে যদি এর আর ভালো সমাধান আসে — আমরা শুধু হুকটার ভিতরের কোড আপডেট করব, কিন্তু যেসব কম্পোনেন্ট হুকটা ব্যবহার করছিল, সেগুলো একদমই অপরিবর্তিত থাকবে। এটা অনেক বড় সুবিধা।


ধাপ ৩: React এর নতুন হেল্পার API দিয়ে করা (আরো ভালো সমাধান)

React 18 থেকে একটা নতুন হুক এসেছে — useSyncExternalStore এটা আমাদের উপরের সব সমস্যার সুন্দর সমাধান দেয়।

import { useSyncExternalStore } from "react";
 
function subscribe(callback) {
  window.addEventListener("online", callback);
  window.addEventListener("offline", callback);
  return () => {
    window.removeEventListener("online", callback);
    window.removeEventListener("offline", callback);
  };
}
 
export function useOnlineStatus() {
  return useSyncExternalStore(
    subscribe,
    () => navigator.onLine, // ক্লায়েন্টে স্টেট পড়া
    () => true // সার্ভারে fallback মান (ধরে নেই always online)
  );
}

তাহলে আমরা কী শিখলাম?

✅ কাস্টম হুক তৈরি করলে কম্পোনেন্টে কিছুই বদলাতে হয় না ✅ হুকের ভিতর চাইলে আগের useEffect রাখুন, চাইলে নতুন API ✅ ভবিষ্যতে React যদি ভালো সমাধান দেয় — শুধু হুক আপডেট করলেই চলবে


একটু ভাবুন

আপনি যদি আপনার অ্যাপের মধ্যে বারবার useEffect লিখে যান, তাহলে সব জায়গায় গিয়ে কোড ঠিক করতে হবে।

আর আপনি যদি সাধারণ কাজগুলো useSomething হুক বানিয়ে ব্যবহার করেন, তাহলে সেই কাজ এক জায়গায় থাকল — যেটা ভবিষ্যতের জন্য অনেক সুবিধাজনক।

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


এক ঝলকে উপকারিতা

  1. কম্পোনেন্ট থাকে পরিষ্কার, সহজ ভাষায়
  2. ইফেক্ট লজিক থাকে এক জায়গায়
  3. ভবিষ্যতে আপডেট দিতে হলে কেবল হুকটা বদলালেই হয়
  4. আপনি নিজেই একটা ছোট লাইব্রেরি বানিয়ে ফেললেন আপনার অ্যাপের জন্য

চল, আমরা সেকশন ২.৫: There is more than one way to do it — বাংলায় সহজ, প্রাঞ্জল এবং ব্যাখ্যাসমৃদ্ধভাবে শুরু করি। যেহেতু এটা শেষ এবং তুলনামূলক বড় সেকশন, তাই আমি একে তিনটি ভাগে ভাগ করে লিখবো:


সেকশন ২.৫ (১ম ভাগ): একের অধিক উপায় থাকে সব কাজের

অনেক সময় তুমি যখন কিছু বানাতে বসো, সেটা বানানোর উপায় একটাই না — বরং একাধিক পথেই সেটা সম্ভব। যেমন ধরো, তুমি চাও কোনো টেক্সট স্ক্রিনে ধীরে ধীরে ফেড-ইন হোক — মানে শুরুতে অপাসিটি থাকবে ০ (অদৃশ্য), তারপর ধীরে ধীরে বাড়তে বাড়তে ১ (সম্পূর্ণ দৃশ্যমান) হবে।

এই কাজটা তুমি শুরু করতে পারো React এর useEffect() হুকের মাধ্যমে, যেখানে তুমি নিজে হাতে একটা এনিমেশন লুপ লিখবে। আমরা এখানে requestAnimationFrame() API ব্যবহার করবো, যা ব্রাউজারের বিল্ট-ইন API — এটা বারবার স্ক্রিনে ফ্রেম আঁকার জন্য কল হয়।

প্রথমে দেখে নিই একটি সাধারণ কোড উদাহরণ:

import { useEffect, useRef } from "react";
 
function Welcome() {
  const ref = useRef(null);
 
  useEffect(() => {
    const node = ref.current;
    const duration = 1000; // এনিমেশন কত সময় চলবে (মিলিসেকেন্ডে)
    let startTime = performance.now();
    let frameId = null;
 
    function onFrame(now) {
      const elapsed = now - startTime;
      const progress = Math.min(elapsed / duration, 1);
      node.style.opacity = progress;
 
      if (progress < 1) {
        frameId = requestAnimationFrame(onFrame);
      }
    }
 
    node.style.opacity = 0;
    frameId = requestAnimationFrame(onFrame);
 
    return () => cancelAnimationFrame(frameId);
  }, []);
 
  return (
    <h1 className="welcome" ref={ref}>
      Welcome
    </h1>
  );
}

এই কোডে যা হচ্ছে:

  • আমরা ref দিয়ে DOM নোড অ্যাক্সেস করছি।
  • তারপর useEffect() এর ভেতরে একটা ফ্রেম-লুপ চালাচ্ছি।
  • প্রতি ফ্রেমে অপাসিটি বাড়ানো হচ্ছে ধীরে ধীরে।
  • যখন অপাসিটি ১ এ পৌঁছায়, তখন এনিমেশন বন্ধ হয়।

আরও পরিষ্কারভাবে লিখি useFadeIn কাস্টম হুক বানিয়ে

তুমি চাও যদি কোডকে আরও রিডেবল এবং পুনঃব্যবহারযোগ্য করতে, তাহলে useFadeIn() নামে একটি কাস্টম হুক বানিয়ে নিতে পারো।

// useFadeIn.js
import { useEffect } from "react";
 
export function useFadeIn(ref, duration = 1000) {
  useEffect(() => {
    const node = ref.current;
    let startTime = performance.now();
    let frameId = null;
 
    function onFrame(now) {
      const elapsed = now - startTime;
      const progress = Math.min(elapsed / duration, 1);
      node.style.opacity = progress;
 
      if (progress < 1) {
        frameId = requestAnimationFrame(onFrame);
      }
    }
 
    node.style.opacity = 0;
    frameId = requestAnimationFrame(onFrame);
 
    return () => cancelAnimationFrame(frameId);
  }, [ref, duration]);
}

এখন আমরা Welcome কম্পোনেন্টে এটি ব্যবহার করবো:

import { useRef } from "react";
import { useFadeIn } from "./useFadeIn";
 
function Welcome() {
  const ref = useRef(null);
  useFadeIn(ref, 1000);
 
  return (
    <h1 className="welcome" ref={ref}>
      Welcome
    </h1>
  );
}

এখন কোডটা অনেক পরিষ্কার হলো, তাই না?


অবশ্যই Md. Mojnu Miah ভাই। নিচে সেকশন ২.৫-এর ২য় ভাগ (There is more than one way to do it) বাংলায় সহজ ভাষায় ব্যাখ্যা করা হলো, যেখানে আমরা দেখছি কীভাবে JavaScript class দিয়ে fade-in animation তৈরি করা যায় এবং কবে CSS ব্যবহার করাই বেশি ভালো:


🧠 ২.৫ এর ২য় ভাগ: ক্লাস দিয়ে এনিমেশন এবং আরও ভালো বিকল্প

আমরা আগের অংশে দেখতে পেলাম কিভাবে useEffect এবং requestAnimationFrame ব্যবহার করে জাভাস্ক্রিপ্টের মাধ্যমে ফেইড-ইন এনিমেশন তৈরি করা যায়।

এখন ভাবুন—এই জটিলতা কি আমরা কমাতে পারি?

✅ সমাধান ৩: JavaScript ক্লাস দিয়ে এনিমেশন তৈরি করা

React এর useEffect হুকের ভিতরে লজিক না রেখে যদি আমরা সব imperative (নির্দেশমূলক) কোড একটা আলাদা ক্লাসে নিয়ে যাই, তাহলে কোডটা অনেক পরিষ্কার হয়।

🧾 FadeInAnimation নামে একটি ক্লাস বানিয়ে আমরা opacity বাড়ানোর কাজটা তার উপর ছেড়ে দিয়েছি:

export class FadeInAnimation {
  constructor(node) {
    this.node = node;
  }
 
  start(duration) {
    this.duration = duration;
    this.onProgress(0);
    this.startTime = performance.now();
    this.frameId = requestAnimationFrame(() => this.onFrame());
  }
 
  onFrame() {
    const timePassed = performance.now() - this.startTime;
    const progress = Math.min(timePassed / this.duration, 1);
    this.onProgress(progress);
 
    if (progress === 1) {
      this.stop();
    } else {
      this.frameId = requestAnimationFrame(() => this.onFrame());
    }
  }
 
  onProgress(progress) {
    this.node.style.opacity = progress;
  }
 
  stop() {
    cancelAnimationFrame(this.frameId);
    this.startTime = null;
    this.frameId = null;
  }
}

🎯 উপকারিতা কী?

  • আপনার useFadeIn হুক ছোট আর পরিষ্কার থাকে।
  • এনিমেশন লজিক ক্লাসের ভেতরে চলে যায় — যেন একটা ছোট এনিমেশন ইঞ্জিন তৈরি হয়েছে।
  • যদি ভবিষ্যতে fade-in ছাড়াও fade-out, bounce, scale ইত্যাদি বানাতে চান — ক্লাস বানিয়ে সহজেই করা যাবে।

✅ সমাধান ৪: CSS দিয়ে fade-in — সবচেয়ে সহজ ও দ্রুত সমাধান

আপনি এত কষ্ট করে JavaScript দিয়ে এনিমেশন বানাচ্ছেন, কিন্তু এমন অনেক এনিমেশন আছে যেগুলো CSS দিয়েই করা যায়। যেমন, fade-in animation।

.welcome {
  animation: fadeIn 1000ms;
}
 
@keyframes fadeIn {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

👉 এই সোজাসাপ্টা CSS animation অনেক দ্রুত, মসৃণ (smooth), এবং performance-friendly।

React component এ:

function Welcome() {
  return <h1 className="welcome">Welcome</h1>;
}

কোনো Hook বা ref লাগে না!


🧠 তাহলে কোনটা বেছে নেব?

পদ্ধতিকবে ব্যবহার করবেন?
JavaScript useEffect দিয়ে এনিমেশনযদি এনিমেশন dynamic হয় বা complex লজিক লাগে
JavaScript ক্লাস দিয়ে এনিমেশনযদি এনিমেশন repeat হয় বা reusable করতে চান
CSS এনিমেশনযদি সহজ এনিমেশন হয় (fade, slide, scale ইত্যাদি) এবং performance দরকার হয়

🔚 এই অংশের মূল কথা: Effect দিয়ে আপনি বাইরের জগতের সাথে React কে সংযুক্ত করেন। কিন্তু অনেক সময় এই Effect গুলোর লজিক বাইরে ক্লাস বা আলাদা হুকে পাঠিয়ে দিলে আপনার কোড অনেক সুন্দর ও পরিষ্কার হয়।

তবে যদি কোনো এনিমেশন CSS দিয়েই হয়, তাহলে JavaScript দিয়ে করতে যাবেন না—“Sometimes, you don’t even need a Hook!”


অবশ্যই Md. Mojnu Miah ভাই! নিচে সেকশন ২.৫: There is more than one way to do it এর ৩য় ও শেষ অংশ বাংলায় সহজভাবে ব্যাখ্যা করা হলো:


✅ ২.৫ এর ৩য় ভাগ: সারাংশ ও টেকঅ্যাও

এই পুরো সেকশনে আমরা দেখলাম—React Component-এর ভিতরে কীভাবে কোন কাজ আমরা অনেকভাবে করতে পারি। React আমাদের শুধু declarative UI লেখার সুবিধা দেয় না, বরং বাইরের "imperative world" এর সঙ্গে যুক্ত হওয়ার সহজ উপায়ও দেয় useEffect এর মাধ্যমে।


📌 মূল পয়েন্টগুলো এক নজরে:

১. Effect দিয়ে বাইরের জগতের সাথে React কে সংযুক্ত করা যায়

React নিজে শুধু UI দেখানোর জন্য কাজ করে। কিন্তু যখন DOM পরিবর্তন, Timer সেট করা, API কল করা, বা এনিমেশন চালানোর মতো বাইরের জগতের কাজ দরকার হয়, তখন useEffect() ব্যবহার করা হয়।

২. React-এর ভিতরে এনিমেশন করার অনেক উপায় আছে

  • useEffect + requestAnimationFrame দিয়ে সরাসরি এনিমেশন চালানো যায়।
  • সেই লজিক এক্সট্র্যাক্ট করে আলাদা JavaScript ক্লাস বা ফাংশনে নেওয়া যায়।
  • অথবা সবচেয়ে ভালো — যদি সম্ভব হয়, CSS দিয়ে এনিমেশন করা উচিত।

৩. JavaScript ক্লাস দিয়ে কোডের জটিলতা কমানো যায়

কোনো কাজ বারবার করতে হলে বা জটিল হলে, সেই কাজের জন্য আলাদা ক্লাস বানালে কোড পরিষ্কার, রিইউজেবল ও টেস্টযোগ্য হয়।

৪. সব সময় Hook ব্যবহার করতে হবে না

অনেক সময় কাজটা CSS বা অন্য উপায়ে করা সম্ভব — তখন অযথা useEffect বা useRef দিয়ে কাজ করা উচিত নয়।


📘 আপনি যা শিখলেন:

React শুধুমাত্র JSX দিয়ে UI তৈরি করার টুল নয়—এটা বাইরের জগতের সাথেও স্মার্টভাবে যোগাযোগ করার একটি মাধ্যম। আর আপনি যখনই কোনো সমস্যা সমাধান করতে যাবেন, মনে রাখবেন:

“There is more than one way to do it.” 👉 আপনার কাজের ধরন বুঝে সঠিক উপায়টা বেছে নিন।


🎯 প্র্যাকটিস টিপ:

React-এ কোনো নতুন ফিচার বা সমস্যা নিয়ে কাজ করার আগে নিজেকে প্রশ্ন করুন:

  • এটা কি purely UI concern?
  • এটা কি time-based বা external system-এর সাথে জড়িত?
  • এটা কি reusable করা যাবে?
  • এটা কি CSS দিয়েই সমাধান করা যায়?

এই প্রশ্নগুলোর উত্তর দিলে আপনি নিজের জন্য সেরা সমাধান বেছে নিতে পারবেন।