useEffect এর আদ্যোপান্ত

useEffect এর আদ্যোপান্ত

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

useEffect কি?

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

যদি আপনি useEffect ব্যবহার করেন তবে কম্পোনেন্ট ডাটার জন্য অপেক্ষা না করেই ইনিশিয়ালি রেন্ডার হয়ে যাবে। useEffect ইনিশিয়াল রেন্ডারিং এর পর রান হবে এবং যাবতীয় সাইড ইফেক্টসমূহ পারফর্ম করবে।

useEffect এর কাজ

useEffect এর কাজ মোটামুটি আগেই বলা হয়েছে। তাও আরো নির্দিষ্টভাবে নিচে দেয়া হলো।

  • কম্পোনেন্ট রেন্ডার হওয়ার পর সাইড ইফেক্ট পারফর্ম করা।

  • useEffect ক্লাস কম্পোনেন্টের লাইফসাইকেল বিহেভিয়ার যেমন componentDidMount, componentDidUpdate এবং componentWillUnmount মিমিক করে।

  • এটি এক্সটার্নাল রিসোর্সের সাথে কম্পোনেন্টের বিহেভিয়ার সিনক্রোনাইজ করে। যখন ডাটা অ্যাভেইলেবল হবে তখন যেন ডাটা সঠিকভাবে ফেচ এবং ডিসপ্লে হয় সেটি নিশ্চিত করে।

useEffect এর ব্যবচ্ছেদ

টেকনিক্যাল এক্সপ্লেনেশনে যাওয়ার আগে প্রথমে একটু সহজভাবে ব্যাখ্যা করার চেষ্টা করি। useEffect কে একটা বাক্স হিসেবে চিন্তা করুন। এবার নিচের স্টেপগুলো ভালমতো চিন্তা করুন।

  • আপনার কাছে একটি বাক্স আছে। কি করতে হবে সেটা আপনাকে বলে দিতে হবে। আপনি সব ইনস্ট্রাকশন লিখে সেই বাক্সের মধ্যে রেখে দিলেন। সেখানে বিভিন্ন সিচুয়েশনের জন্য ইনস্ট্রাকশন লেখা আছে।

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

  • যদি কিছু না ঘটে তবে বাক্স বন্ধ থাকবে এবং কিছুই করবে না।

  • সবশেষে যদি আপনার কোনো রুল প্রয়োজন না হয় আপনি বাক্সকে সেভাবে ইনস্ট্রাকশন দিয়ে দিতে পারেন যে আপনি এই রুল স্টপ করে দিতে চাইছেন। বাক্স বন্ধ হয়ে যাবে এবং সেই রুল স্টপ করে দিবে।

এবার টেকনিক্যাল এক্সপ্লেনেশনে আসি। useEffect এর তিনটা পার্ট। একটা হলো কি করতে হবে তার বিবরণ, যদি কোনো কাজ বন্ধ করতে হয় সেটার জন্য ক্লিনআপ ফাংশন (এটি অপশনাল। থাকতেও পারে নাও থাকতে পারে) এবং একটা ডিপেন্ডেন্সি অ্যারে। অর্থাৎ componentDidMount, componentWillUnmount এবং componentDidUpdate

useEffect(() => {
    // The code that we want to run

    // Optional cleanup return function
}, []); // dependency array

এবার একটি উদাহরণ দেখি আমরা।

import React, { useState, useEffect } from 'react';

function DataFetching() {
  const [data, setData] = useState(null);

  useEffect(() => {
    // The code that we want to run
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data));

    // No need to write any cleanup function for this situation
  }, []); // Empty dependency array, runs after the initial render

  return (
    <div>
      {data ? <p>{data}</p> : <p>Loading...</p>}
    </div>
  );
}

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

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

import React, { useState, useEffect } from 'react';

function Timer() {
  const [time, setTime] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setTime(prevTime => prevTime + 1);
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  }, []); // Empty dependency array, runs after the initial render

  return (
    <div>
      <p>Time: {time} seconds</p>
    </div>
  );
}

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

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

function Counter() {
    const [count, setCount] = useState(0);

    useEffect(() => {
        console.log(`The count is now ${count}`);
    }, [count]);

    return (
        <div>
            <p>{count}</p>
            <button onClick={() => setCount(count + 1)}>Increment</button>
            <button onClick={() => setCount(count - 1)}>Decrement</button>
        </div>
    );
}

এখানে তেমন কোনো কমপ্লেক্স উদাহরণ নয়। আমরা বাটনে ক্লিক করলে useEffect এর ভেতরের কোড রান হবে। যদি আমরা ডিপেন্ডেন্সি খালি রাখতাম তবে আপনি কনসোলে দেখতে পেতেন শুধু The count is now 0 অর্থাৎ প্রথম রেন্ডারিং এর পর শুধুমাত্র একবারই রান হতো। এখন এখানে ডিপেন্ডেন্সি হিসেবে আমরা দিয়েছি count অর্থাৎ count এর ভ্যালু পরিবর্তন হলেই useEffect রান হবে। সেক্ষেত্রে আমরা প্রত্যেক চেইঞ্জের ভ্যালু কনসোলে দেখতে পাবো।

আশা করি useEffect নিয়ে আপনাদের ধারণা ক্লিয়ার করতে পেরেছি। এরপরও যদি কোনো কিছু মিস হয়ে থাকে কমেন্টে জানাতে পারেন।