Responding To Events

Responding To Events

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

যেমন:

  • বাটনে ক্লিক করা → কোনো মেসেজ দেখানো
  • ইনপুট ফিল্ডে টাইপ করা → ইনপুটের মান আপডেট করা
  • মাউসের কার্সর কোথাও রাখা → ডিজাইন পরিবর্তন করা

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

কম্পোনেন্টে ইভেন্ট হ্যান্ডলার যোগ করার সহজ উপায়

প্যারেন্ট কম্পোনেন্ট থেকে চাইল্ড কম্পোনেন্টে ইভেন্ট হ্যান্ডলিং পাঠানোর পদ্ধতি

ইভেন্ট কীভাবে ছড়িয়ে পড়ে (Propagation) এবং তা কীভাবে বন্ধ করা যায়

ডিফল্ট ব্রাউজার আচরণ বন্ধ করা (Preventing Default Behavior)


কিভাবে কম্পোনেন্টে Event Handlers এড করব?

উদাহরণস্বরূপ, নিচের বাটনটি এখনো কিছুই করে না:

export default function Button() {
  return <button>আমি কিছু করি না</button>;
}

একজন ইউজার বাটনে ক্লিক করলে আপনি এই ০৩ টি ধাপ অনুসরণ করে তাকে একটি মেসেজ দেখাতে পারেন-

  1. Button কম্পোনেন্টের মধ্যে একটি handleClick নামে একটি ফাংশন ডিফাইন করুন।
  2. আপনার তৈরি করা ফাংশনের ভিতরে মেসেজ দেখানোর লজিক ডেভেলপ করুন। (alert("message"))
  3. <button> JSX এ onClick={handleClick} এড করুন।

(গিটহাবে কোড দেখুন)[https://github.com/codebymojnu/react-book-code/blob/main/src/App.jsx (opens in a new tab)]

// Add Event Handelar in JSX
 
export default function App() {
  // Define handleClick function
  function handleClick() {
    // develop your logic here
    alert("Why you click me!");
  }
 
  // add event handler to button JSX
  return (
    <div className="flex justify-center">
      <button
        onClick={handleClick}
        className="px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 transition-all cursor-pointer"
      >
        Click Me
      </button>
    </div>
  );
}

আমরা handleClick ফাংশনটি ডিফাইন করেছি এবং এটি props হিসেবে button এ পাশ করেছি। এখানে handleClick একটি ইভেন্ট হ্যান্ডেলারEvent Handler ফাংশনগুলোঃ

  1. সাধারণত Component এর ভিতরে event handler যোগ করা হয়।
  2. নামের শুরুতে handle এবং তারপর ইভেন্টের নাম থাকে।
  3. প্রচলিত রীতি অনুযায়ী, ইভেন্ট হ্যান্ডলারগুলোর নাম handle দিয়ে শুরু হয় এবং তারপর ইভেন্টের নাম যোগ করা হয়। আমরা প্রায়ই onClick={handleClick}, onMouseEnter={handleMouseEnter} ইত্যাদি দেখতে পাবো।

বিকল্পভাবে, JSX এর মধ্যেই একটি ইনলাইন ইভেন্ট হ্যান্ডেলার ডিফাইন করতে পারি:

<button
  onClick={function handleClick() {
    alert("আপনি আমাকে ক্লিক করেছেন!");
  }}
></button>

অথবা, আরও সংক্ষিপ্তভাবে, একটি এ্যারো ফাংশন ব্যবহার করে:

<button
  onClick={() => {
    alert("আপনি আমাকে ক্লিক করেছেন!");
  }}
></button>

সবগুলো স্টাইলই সঠিক।

ছোট ফাংশনের জন্য ইনলাইন ইভেন্ট হ্যান্ডলার সুবিধাজনক।

ইভেন্ট হ্যান্ডলারে ফাংশন পাস করার সঠিক ও ভুল পদ্ধতি

ইভেন্ট হ্যান্ডলারগুলোকে সঠিকভাবে ব্যবহার করা গুরুত্বপূর্ণ। অন্যথায়, আপনার অ্যাপ্লিকেশন আশাতীত কাজ করবে না।

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

ঠিক পদ্ধতি: ফাংশন পাস করাভুল পদ্ধতি: ফাংশন কল করাব্যাখ্যা
<button onClick={handleClick}>ক্লিক করুন</button><button onClick={handleClick()}>ক্লিক করুন</button>handleClick ফাংশনটি onClick ইভেন্ট হ্যান্ডলারকে পাস করা হয়েছে। যখন বাটনটি ক্লিক করা হয়, তখন handleClick ফাংশনটি কল হবে।
এখানে handleClick একটি ফাংশন।এখানে handleClick() ফাংশনটি কল করা হচ্ছে এবং এর রিটার্ন মান পাস করা হচ্ছে।ফাংশনকে ডাকলে ফাংশনের কাজ শেষ হয়ে যায় এবং ইভেন্ট হ্যান্ডলার কাজ করবে না।

সঠিক পদ্ধতিঃ

<button onClick={() => alert("বাটনে ক্লিক করা হয়েছে")}>ক্লিক করুন</button>

এখানে একটি অ্যানোনিমাস ফাংশন তৈরি করা হয়েছে এবং তা onClick ইভেন্ট হ্যান্ডলার হিসাবে পাস করা হয়েছে।

ভুল পদ্ধতিঃ

<button onClick={alert("বাটন ক্লিক করা হয়েছে")}>ক্লিক করুন</button>

এখানে alert ফাংশনটি অবিলম্বে কল করা হবে এবং বাটন ক্লিক করার আগেই একটি অ্যালার্ট দেখাবে।

সারসংক্ষেপ

  • ইভেন্ট হ্যান্ডলারের প্রোপস একটি ফাংশন হিসাবে পাস করুন।
  • ইনলাইন কোড লিখলে অ্যানোনিমাস ফাংশন ব্যবহার করুন।

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

ইভেন্ট হ্যান্ডেলার কম্পোনেন্টে আসা প্রপস রিড করতে পারে

ইভেন্ট হ্যান্ডলারগুলো একটি কম্পোনেন্টের অভ্যন্তরে ঘোষিত হয়, তাই কম্পোনেন্টে আসা সকল প্রপসগুলিতে ইভেন্ট হ্যান্ডেলারগুলোর অ্যাক্সেস থাকে। Event handler can reads components props.

নিচের কোডটি খেয়াল করুন। এখানে একটি বাটন রয়েছে যা ক্লিক করলে তার মেসেজ প্রপটির সাথে একটি অ্যালার্ট দেখায়:

function AlertButton({ message, children }) {
  return <button onClick={() => alert(message)}>{children}</button>;
}
 
export default function Toolbar() {
  return (
    <div>
      <AlertButton message="Playing!">   সিনেমা প্লে করুন</AlertButton>
      <AlertButton message="Uploading!">ইমেজ আপলোড করুন</AlertButton>  
    </div>
  );
}

এটি এই দুটি বাটনে ভিন্ন বার্তা শো করতে দেয়। তাদের কাছে পাস করা বার্তাগুলো চেঞ্জ করে দেখুন।

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

প্রায়শই আমাদের দরকার পড়তে পারে, প্যারেন্ট কম্পোনেন্টের ইভেন্ট হ্যান্ডেলারটি তার চাইল্ড কম্পোনেন্টের JSX থেকে নিয়ন্ত্রণ করতে। তখন আমরা Parent এর ইভেন্ট হ্যান্ডেলারটি Child Component এ পাঠিয়ে দিব, then চাইল্ড কম্পোনেন্টের JSX থেকে হ্যান্ডলারটি নিয়ন্ত্রন হবে।

প্যারেন্ট থেকে প্রপসকে ইভেন্ট হ্যান্ডলার হিসাবে নিম্নলিখিতভাবে পাস করুন:

গিটহাবে কোড দেখুন: https://github.com/codebymojnu/react-book-code/blob/2.2/src/App.jsx (opens in a new tab)

// Add Event Handelar in JSX
 
function AlertButton({ handleClick, children }) {
  return (
    <button
      onClick={handleClick}
      className="px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 transition-all cursor-pointer"
    >
      {children}
    </button>
  );
}
 
export default function App() {
  // Define event handler
  function handleClick(message) {
    if (message === "Play") {
      alert("Playing Movie...");
    } else {
      alert("Uploading Image...");
    }
  }
  return (
    <div className="flex justify-center mt-4 gap-3">
      <AlertButton handleClick={() => handleClick("Play")}>
        Play this Movie
      </AlertButton>
      <AlertButton handleClick={() => handleClick("Uplaod")}>
        Upload Image
      </AlertButton>
    </div>
  );
}

চাইল্ড কম্পোনেন্টে Event Handler পাঠাতে props নামকরণ

ইভেন্ট হ্যান্ডেলার প্রপসের নামকরণের ক্ষেত্রে খেয়াল রাখা উচিত HTML এর onClick, onChange reserve keyword ব্যবহার না করাই ভালো।

<button> এবং <div> এর মতো বিল্ট-ইন HTML কম্পোনেন্টগুলো শুধুমাত্র onClick এর মতো ব্রাউজার ইভেন্ট নামগুলোকেই প্রপস হিসেবে সাপোর্ট করে। কিন্তু, যখন আপনি নিজের কম্পোনেন্ট তৈরি করবেন, তখন আপনি ইচ্ছামতো তাদের ইভেন্ট হ্যান্ডলার প্রপসগুলোর নামকরণ করতে পারবেন।

সুপারিশ অনুযায়ী, ইভেন্ট হ্যান্ডলার প্রপসগুলো on দিয়ে শুরু হওয়া উচিত, তারপর একটি বড় হাতের অক্ষর থাকবে।

উদাহরণস্বরূপ, Button কম্পোনেন্টের onClick প্রপটিকে onSmash নাম দেওয়া যেতে পারত:

export default function App() {
  return (
    <Toolbar
      onPlayMovie={() => alert("Playing!")}
      onUploadImage={() => alert("Uploading!")}
    />
  );
}
 
function Toolbar({ onPlayMovie, onUploadImage }) {
  return (
    <div>
      <Button onSmash={onPlayMovie}>Play Movie</Button>
      <Button onSmash={onUploadImage}>Upload Image</Button>
    </div>
  );
}
 
function Button({ onSmash, children }) {
  return <button onClick={onClick}>{children}</button>;
}

ইভেন্টের বি-স্তা-র (Event Propagation)

React-এ, যখন কোনো একটি ইভেন্ট ট্রিগার হয় (যেমন onClick ইভেন্ট), তখন সেটি শুধু নির্দিষ্ট এলিমেন্টেই সীমাবদ্ধ থাকে না। ইভেন্ট হ্যান্ডেলারটি ধাপে ধাপে তার প্যারেন্ট এলিমেন্ট পর্যন্ত পৌঁছাতে পারে, এটাকেই ইভেন্ট বাবলিং (Event Bubbling) বলা হয়। অর্থাৎ, ইভেন্ট যেখান থেকে শুরু হয়, সেখান থেকে উপরের দিকে ছড়িয়ে পড়ে।

উদাহরণ দিয়ে বুঝি

নিচের কোডে একটি <div> রয়েছে, যার মধ্যে দুটি <button> রয়েছে।

export default function Toolbar() {
  return (
    <div
      className="Toolbar"
      onClick={() => {
        alert("আপনি টুলবারে ক্লিক করেছেন!");
      }}
    >
      <button onClick={() => alert("প্লে হচ্ছে!")}>মুভি প্লে করুন</button>
      <button onClick={() => alert("আপলোড হচ্ছে!")}>ছবি আপলোড করুন</button>
    </div>
  );
}

এখানে তিনটি onClick হ্যান্ডলার ব্যবহার করা হয়েছে:

  1. প্রতিটি <button>-এর জন্য আলাদা হ্যান্ডলার
  2. প্যারেন্ট <div>-এর জন্য আলাদা হ্যান্ডলার

কীভাবে ইভেন্ট প্রোপাগেট হয়ে যায়?

  • যদি আপনি "মুভি প্লে করুন" বাটনে ক্লিক করেন, তাহলে প্রথমে বাটনের onClick হ্যান্ডলার কাজ করবে ("প্লে হচ্ছে!" দেখাবে)।
  • এরপর, একই ইভেন্ট <div>-এর onClick হ্যান্ডলার পর্যন্ত পৌঁছে যাবে এবং "আপনি টুলবারে ক্লিক করেছেন!" মেসেজ দেখাবে।
  • অর্থাৎ, দুটি অ্যালার্ট বক্স আসবে।

একইভাবে, যদি আপনি "ছবি আপলোড করুন" বাটনে ক্লিক করেন, তাহলে

  1. প্রথমে "আপলোড হচ্ছে!"
  2. তারপর দেখাবে "আপনি টুলবারে ক্লিক করেছেন!"

যদি শুধু টুলবারে ক্লিক করা হয়?

তাহলে শুধুমাত্র <div>-এর onClick ইভেন্ট চলবে এবং "আপনি টুলবারে ক্লিক করেছেন!" দেখাবে।

Pitfall

React-এ প্রায় সব ইভেন্টই বাবল হয়, কিন্তু onScroll ইভেন্ট ব্যতিক্রম। এটি শুধুমাত্র যে JSX এলিমেন্টে সংযুক্ত থাকে, সেখানে কাজ করে, প্যারেন্ট এলিমেন্টে ছড়িয়ে পড়ে না।

  • React-এ ইভেন্ট সাধারণত প্যারেন্ট পর্যন্ত ছড়িয়ে পড়ে (Event Bubbling)।
  • যদি (child) এলিমেন্টের ইভেন্ট কাজ করে, তাহলে সেটির প্যারেন্টের ইভেন্টেও ট্রিগার হতে পারে।
  • onScroll ইভেন্ট বাবল করে না।

বিস্তার বন্ধ করা (Stopping Propagation)

কখনো কখনো, আমরা চাই না যে ইভেন্ট প্যারেন্ট পর্যন্ত পৌঁছাক। এজন্য আমরা e.stopPropagation() ব্যবহার করতে পারি।

function Button({ handleClick, children }) {
  return (
    <button
      onClick={(e) => {
        e.stopPropagation(); // ইভেন্ট প্যারেন্টে ছড়ানো বন্ধ করবে
        handleClick();
      }}
    >
      {children}
    </button>
  );
}

এখন, যখন বাটনে ক্লিক করবেন, তখন শুধুমাত্র বাটনের onClick চলবে, কিন্তু div-এর onClick চলবে না।

ক্যাপচার ফেজ ইভেন্ট (Capture phase events)

কিছু ক্ষেত্রে, আমরা চাই ইভেন্ট প্যারেন্ট আগে ট্রিগার হোক। এটি করতে হলে onClickCapture ব্যবহার করতে হবে।

<div
  onClickCapture={() => {
    alert("এটি প্রথমে চলবে!");
  }}
>
  <button onClick={(e) => e.stopPropagation()} />
  <button onClick={(e) => e.stopPropagation()} />
</div>

এখানে, onClickCapture প্রথমে চলবে, তারপর onClick।

ক্যাপচার ফেজ সাধারণত ব্যবহার করা হয় না, তবে এটি জানা ভালো যে এটি আছে।

নোট করুন যে এই ক্লিক হ্যান্ডলার কিছু কোড চালায় এবং তারপর প্যারেন্ট দ্বারা পাঠানো onClick প্রপটিকে কল করে:

function Button({ onClick, children }) {
  return (
    <button
      onClick={(e) => {
        e.stopPropagation();
        onClick();
      }}
    >
      {children}
    </button>
  );
}

১ টি সমস্যা:

ধরা যাক, আমাদের একটি টেবিল আছে, যেখানে প্রত্যেক রো-এ ইউজারের ইনফরমেশন রয়েছে। টেবিলের কলামগুলো হলো:

  • নাম
  • মোবাইল নাম্বার
  • ডিলেট বাটন

আমরা চাই, ইউজার যখন টেবিলের যে-কোনো রো-তে ক্লিক করবে, তখন সেই রো-এ থাকা নির্দিষ্ট ইউজারের প্রোফাইল দেখা যাবে, যেমন:

  • তার সমস্ত ডাটা
  • মাসিক ইনকাম
  • ইমেইল
  • তার জব রোল

তবে, আমাদের লক্ষ্য হলো ডিলেট বাটনে ক্লিক করলে ইউজারের প্রোফাইল না দেখানো, বরং ওই রো বা ইউজারকে ডিলেট করা

Solution Link: https://github.com/codebymojnu/react-book-code/blob/exercise-2.1-solution/src/App.jsx (opens in a new tab)

ডিফল্ট ব্রাউজার আচরণ বন্ধ করা (Preventing Default Behavior)

কিছু ইভেন্টের ডিফল্ট ব্রাউজার অ্যাকশন থাকে। যেমন, <form> সাবমিট করলে পুরো পেজ রিলোড হয়।

// App.js
export default function Signup() {
  return (
    <form onSubmit={() => alert("সাবমিট করা হচ্ছে!")}>
      <input />
      <button>পাঠান</button>
    </form>
  );
}

আপনি এটি থেকে বিরত থাকার জন্য ইভেন্ট অবজেক্টে e.preventDefault() কল করতে পারেন:

// App.js
export default function Signup() {
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        alert("সাবমিট করা হচ্ছে!");
      }}
    >
      <input />
      <button>পাঠান</button>
    </form>
  );
}

e.stopPropagation() এবং e.preventDefault() কে বিভ্রান্ত করবেন না। তারা উভয়ই উপযোগী, কিন্তু একে অপরের সাথে সম্পর্কহীন:

  • e.stopPropagation() উপরের ট্যাগগুলিতে সংযুক্ত ইভেন্ট হ্যান্ডলারগুলিকে ফায়ার হওয়া থেকে বিরত রাখে।
  • e.preventDefault() কিছু ইভেন্টের ডিফল্ট ব্রাউজার আচরণকে প্রতিরোধ করে।

ইভেন্ট হ্যান্ডলারের সাইড ইফেক্ট

অবশ্যই! ইভেন্ট হ্যান্ডলারের সাইড ইফেক্ট থাকতে পারে।

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


সংক্ষেপ (ইভেন্ট হ্যান্ডলিং সহজ ভাষায়)

  • আপনি একটি ফাংশনকে প্রপ হিসেবে পাস করে ইভেন্ট হ্যান্ডেল করতে পারেন, যেমন <button onClick={handleClick}>
  • ইভেন্ট হ্যান্ডলার পাস করতে হবে, কল করা যাবে না!
    • সঠিক: onClick={handleClick}
    • ভুল: onClick={handleClick()}
  • ইভেন্ট হ্যান্ডলার আলাদাভাবে বা ইনলাইন উভয়ভাবেই লেখা যায়।
    • আলাদা ফাংশন:
      function handleClick() {
        console.log("Button clicked!");
      }
    • ইনলাইন ফাংশন:
      <button onClick={() => console.log("Button clicked!")}>Click me</button>
  • কম্পোনেন্টের ভেতরে ইভেন্ট হ্যান্ডলার থাকলে, তা প্রপ্স (props) ব্যবহার করতে পারে।
  • একটি প্যারেন্ট কম্পোনেন্টে ইভেন্ট হ্যান্ডলার রেখে, চাইল্ড কম্পোনেন্টে প্রপ হিসেবে পাঠানো যায়।
  • আপনার প্রয়োজন অনুযায়ী ইভেন্ট হ্যান্ডলারের জন্য কাস্টম নাম ব্যবহার করতে পারেন।
  • ইভেন্ট ব্রাউজারের মাধ্যমে উপরের দিকে ছড়িয়ে পড়ে।
    • এটি বন্ধ করতে e.stopPropagation() ব্যবহার করুন।
  • কিছু ইভেন্টের ডিফল্ট ব্রাউজার আচরণ বন্ধ করতে e.preventDefault() ব্যবহার করুন।
    • যেমন, ফর্ম সাবমিট করলে পেজ রিফ্রেশ হওয়া বন্ধ করা:
      function handleSubmit(e) {
        e.preventDefault();
        console.log("Form submitted!");
      }
  • চাইল্ড কম্পোনেন্ট থেকে সরাসরি প্যারেন্টের ইভেন্ট হ্যান্ডলার কল করা ভালো বিকল্প হতে পারে।