নিচে আপনার দেয়া লেখার সহজ, পরিষ্কার এবং সরল বাংলায় অনুবাদ করা হলো:


শিরোনাম: রেফ (Ref) ব্যবহার করে মান সংরক্ষণ করা

🔰 পরিচিতি

যখন আপনি চান কম্পোনেন্ট কোনো তথ্য “মনে রাখুক”, কিন্তু সেই তথ্য যেন নতুন করে রেন্ডার না করায়, তখন আপনি ref ব্যবহার করতে পারেন।


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

  • কীভাবে আপনার কম্পোনেন্টে একটি ref যোগ করবেন
  • কীভাবে ref-এর মান আপডেট করবেন
  • কীভাবে ref state থেকে আলাদা
  • কীভাবে ref নিরাপদভাবে ব্যবহার করবেন

কীভাবে কম্পোনেন্টে ref যোগ করবেন

আপনার কম্পোনেন্টে ref যোগ করতে হলে প্রথমে React থেকে useRef হুক ইম্পোর্ট করতে হবে:

import { useRef } from "react";

তারপর কম্পোনেন্টের ভিতরে useRef কল করে প্রাথমিক মান পাস করতে হবে। যেমন এখানে 0 মান দিয়ে একটি ref তৈরি করা হয়েছে:

const ref = useRef(0);

useRef একটি অবজেক্ট রিটার্ন করে এরকম:

{
  current: 0; // আপনি যে মান দিয়েছিলেন সেটি এখানে থাকে
}

➡️ আপনি ref.current দিয়ে সেই মানে এক্সেস করতে পারবেন। ➡️ এই মান পরিবর্তনযোগ্য (mutable), মানে আপনি এটা পড়তেও পারবেন, আবার চাইলে লিখতেও পারবেন।

এটা এমন একটা গোপন জায়গার মতো, যেটা React ট্র্যাক করে না। (এটাই এক ধরনের Escape Hatch — React-এর নিয়ম ভেঙে কাজ করার সুযোগ!)


🔘 একটি বাটন ক্লিক করলে কীভাবে ref বাড়ানো যায়:

import { useRef } from "react";
 
export default function Counter() {
  let ref = useRef(0);
 
  function handleClick() {
    ref.current = ref.current + 1;
    alert("You clicked " + ref.current + " times!");
  }
 
  return <button onClick={handleClick}>Click me!</button>;
}

এই ref 0 নাম্বারে পয়েন্ট করছে, কিন্তু আপনি চাইলে string, object বা function যেকোনো কিছুকেই ref দিয়ে রাখতে পারেন।


⚠️ মনে রাখবেন:

  • প্রতিবার ref বাড়লেও কম্পোনেন্ট রি-রেন্ডার হয় না।

  • ref-এর মতো state-ও React ধরে রাখে (retain করে) রি-রেন্ডারের মাঝে।

  • কিন্তু state বদলালে কম্পোনেন্ট রি-রেন্ডার হয়, আর ref বদলালে হয় না!

    এখানে টাইমার (stopwatch) বানানোর একটা উদাহরণ দেওয়া হয়েছে, যেখানে ref আর state একসাথে ব্যবহার করা হয়েছে।


নিচে আমি সম্পূর্ণ টেক্সটটা সহজ বাংলায় অনুবাদ করেছি React-এর Stopwatch উদাহরণসহ:


🕒 উদাহরণ: স্টপওয়াচ বানানো

তুমি ref আর state একসাথে একটি কম্পোনেন্টে ব্যবহার করতে পারো। এই উদাহরণে আমরা একটি স্টপওয়াচ বানাবো যেটা ইউজার Start/Stop বাটনে চাপ দিয়ে চালু বা বন্ধ করতে পারবে।

যখন ইউজার "Start" চাপবে, তখন কত সময় পেরিয়ে গেছে সেটা দেখানোর জন্য আমাদের জানতে হবে:

  • কখন Start বাটনে চাপ দেওয়া হয়েছে (startTime)
  • এখন কত সময় চলছে (now)

এই তথ্যগুলো UI-তে দেখানো হয়, তাই এগুলোকে state-এ রাখতে হবে:

const [startTime, setStartTime] = useState(null);
const [now, setNow] = useState(null);

✅ Start চাপলে কী হবে?

তুমি setInterval (opens in a new tab) ব্যবহার করবে, যাতে প্রতি 10 মিলিসেকেন্ড পরপর সময় আপডেট হয়:

import { useState } from "react";
 
export default function Stopwatch() {
  const [startTime, setStartTime] = useState(null);
  const [now, setNow] = useState(null);
 
  function handleStart() {
    setStartTime(Date.now());
    setNow(Date.now());
 
    setInterval(() => {
      setNow(Date.now());
    }, 10);
  }
 
  let secondsPassed = 0;
  if (startTime != null && now != null) {
    secondsPassed = (now - startTime) / 1000;
  }
 
  return (
    <>
      <h1>সময় পেরিয়েছে: {secondsPassed.toFixed(3)} সেকেন্ড</h1>
      <button onClick={handleStart}>Start</button>
    </>
  );
}

🛑 Stop চাপলে কী হবে?

"Stop" চাপলে setInterval() দিয়ে তৈরি করা টাইমার বন্ধ করতে হবে, যাতে now আর আপডেট না হয়। এই কাজ করার জন্য clearInterval (opens in a new tab) ব্যবহার করতে হয়।

তবে টাইমার বন্ধ করতে হলে তোমাকে setInterval যেই ID দিয়েছে সেটা সংরক্ষণ করতে হবে।

এই ID UI-তে দেখাতে হয় না, তাই এটা ref-এ রাখা যাবে:

import { useState, useRef } from "react";
 
export default function Stopwatch() {
  const [startTime, setStartTime] = useState(null);
  const [now, setNow] = useState(null);
  const intervalRef = useRef(null); // টাইমার ID রাখার জন্য
 
  function handleStart() {
    setStartTime(Date.now());
    setNow(Date.now());
 
    clearInterval(intervalRef.current); // আগেরটা বন্ধ করো
    intervalRef.current = setInterval(() => {
      setNow(Date.now());
    }, 10);
  }
 
  function handleStop() {
    clearInterval(intervalRef.current); // স্টপ চাপলে টাইমার বন্ধ
  }
 
  let secondsPassed = 0;
  if (startTime != null && now != null) {
    secondsPassed = (now - startTime) / 1000;
  }
 
  return (
    <>
      <h1>সময় পেরিয়েছে: {secondsPassed.toFixed(3)} সেকেন্ড</h1>
      <button onClick={handleStart}>Start</button>
      <button onClick={handleStop}>Stop</button>
    </>
  );
}

🧭 কখন state, কখন ref?

🔵 যখন কোনো তথ্য UI-তে দেখাতে হয় বা তার পরিবর্তনে রি-রেন্ডার দরকার হয় — state ব্যবহার করো। 🟡 যখন কোনো তথ্য শুধু event handler বা logic-এ দরকার হয়, কিন্তু UI-তে দেখানোর প্রয়োজন নেই — ref ব্যবহার করো।


🔍 পার্থক্যঃ ref বনাম state

বিষয়refstate
ব্যবহারuseRef() দিয়ে তৈরি হয়useState() দিয়ে তৈরি হয়
রেন্ডার ট্রিগার করে?নাহ্যাঁ
মান পরিবর্তন করলে কী হয়?সরাসরি .current মান বদলানো যায়setState() ব্যবহার করে মান বদলাতে হয়
UI-তে ব্যবহার করা যায়?না, করলে সমস্যা হতে পারেহ্যাঁ, প্রতিটি রেন্ডার-এ নির্দিষ্ট স্ন্যাপশট থাকে
কবে ব্যবহার উপযোগী?event handler, DOM reference ইত্যাদিতেUI পরিবর্তনের জন্য

⛔ যদি শুধু ref দিয়ে কাউন্টার বানাও...

import { useRef } from "react";
 
export default function Counter() {
  let countRef = useRef(0);
 
  function handleClick() {
    countRef.current = countRef.current + 1;
  }
 
  return (
    <button onClick={handleClick}>You clicked {countRef.current} times</button>
  );
}

এই কোডে ref ব্যবহারে সংখ্যা বাড়লেও স্ক্রিন আপডেট হবে না, কারণ React বুঝবে না কিছু বদলেছে।

✅ তাই যেটা UI-তে দেখানো লাগে, সেটা state দিয়ে করতে হবে।


আপনি চাইলে এক কম্পোনেন্টে ref এবং state একসাথে ব্যবহার করতে পারেন। ধরুন, আপনি একটা স্টপওয়াচ বানাতে চান যেটা ইউজার "Start" প্রেস করলে শুরু হবে, আর "Stop" দিলে থেমে যাবে।

🧠 যেসব তথ্য রেন্ডারে লাগে, সেগুলো রাখতে হবে state-এ:

const [startTime, setStartTime] = useState(null);
const [now, setNow] = useState(null);
  • startTime: কখন Start চাপা হয়েছিল।
  • now: বর্তমান সময়।

▶️ Start বোতামে ক্লিক করলে কী হবে?

প্রতি 10 মিলিসেকেন্ডে সময় আপডেট করতে হবে, এজন্য setInterval() ব্যবহার করা হয়েছে:

function handleStart() {
  setStartTime(Date.now());
  setNow(Date.now());
 
  setInterval(() => {
    setNow(Date.now());
  }, 10);
}

গুরুত্বপূর্ণ: এখানে setNow() রেন্ডার ট্রিগার করে, তাই টাইম আপডেট হয়ে ইউআই-তে দেখায়।


❌ কিন্তু এখানে সমস্যা আছে!

প্রতি বার Start চাপলে নতুন setInterval() তৈরি হচ্ছে, আর আগেরটা বন্ধ হচ্ছে না। তাই অনেকগুলো interval চলতে থাকে, যা bug হতে পারে।


✅ সমাধান: ref দিয়ে interval ID রাখা

setInterval() যখন কল করেন, সে একটা আইডি দেয় যেটা দিয়ে পরে বন্ধ করা যায় clearInterval() দিয়ে। এই আইডি ইউআই-তে দেখানোর দরকার নেই, তাই এটাকে ref-এ রাখা নিরাপদ।

const intervalRef = useRef(null);

🔁 ঠিক করা Start ও Stop ফাংশন:

function handleStart() {
  setStartTime(Date.now());
  setNow(Date.now());
 
  clearInterval(intervalRef.current); // পুরোনো টাইমার বন্ধ
  intervalRef.current = setInterval(() => {
    setNow(Date.now());
  }, 10);
}
 
function handleStop() {
  clearInterval(intervalRef.current);
}

🧠 উপসংহার:

তথ্যকোথায় রাখবেনকারণ
ইউআই-তে দেখাবেনstateরেন্ডার দরকার
শুধু ভিতরে রাখবেনrefরেন্ডার দরকার নেই

🧠 clearInterval() কী?

setInterval() একটি ফাংশন যা বারবার একটি কাজ চালায় নির্দিষ্ট সময় পর পর (যেমন প্রতি 10ms, প্রতি 1s)। এই কাজ বন্ধ করতে হলে, clearInterval() ব্যবহার করতে হয়।


⚙️ কিভাবে কাজ করে?

  1. তুমি যখন setInterval() ব্যবহার করো, তখন এটি একটি ID (সংখ্যা) রিটার্ন করে।
  2. সেই ID ব্যবহার করে তুমি clearInterval(ID) দিয়ে সেই টাইমার বন্ধ করতে পারো।

✅ উদাহরণ:

const intervalId = setInterval(() => {
  console.log("ঘড়ি টিক টিক করছে...");
}, 1000);
 
// ৫ সেকেন্ড পর টিক টিক বন্ধ করে দেবে
setTimeout(() => {
  clearInterval(intervalId); // টাইমার বন্ধ
  console.log("ঘড়ি থেমে গেছে।");
}, 5000);

🔁 যদি clearInterval() না দাও?

তাহলে setInterval() চালু থাকবে চিরকাল, যতক্ষণ না তুমি ব্রাউজার রিফ্রেশ করো বা পেজ ছাড়ো।


📌 React এর ক্ষেত্রে:

React কম্পোনেন্টে যদি তুমি setInterval() দাও কিন্তু clearInterval() না দাও, তাহলে:

  • একাধিক টাইমার তৈরি হতে পারে (memory leak হয়),
  • বারবার re-render হতে পারে,
  • পারফরম্যান্স খারাপ হয়ে যেতে পারে।

🔍 সংক্ষেপে মনে রাখো:

কাজফাংশন
টাইমার চালু করাsetInterval()
টাইমার বন্ধ করাclearInterval()

তোমার কোডে যদি টাইমার বন্ধ করার প্রয়োজন থাকে (যেমন Stop বাটনে), তাহলে clearInterval() না দিলে টাইমার চলতেই থাকবে।

নিচের অংশটি বাংলায় অনুবাদ করা হলো:


💡 useRef ভিতরে কিভাবে কাজ করে?

যদিও useState এবং useRef দুটোই React থেকে পাওয়া যায়, তবুও ধারণাগতভাবে useRef আসলে useState-এর উপরে তৈরি করা যেত। তুমি কল্পনা করতে পারো, React-এর ভিতরে useRef এইরকমভাবে কাজ করে:

// React-এর ভিতরে
function useRef(initialValue) {
  const [ref, unused] = useState({ current: initialValue });
  return ref;
}

প্রথমবার রেন্ডার হওয়ার সময়, useRef { current: initialValue } রিটার্ন করে। React এই অবজেক্টটি সংরক্ষণ করে রাখে, যাতে পরবর্তী রেন্ডারে আবার একই অবজেক্ট ফেরত দেওয়া যায়। উপরের কোডে দেখো, useState যেটা set করার ফাংশন দেয়, সেটা এখানে ব্যবহারই করা হয়নি—কারণ useRef সবসময় একই অবজেক্ট ফেরত দেয়!

React আমাদের জন্য useRef বিল্ট-ইন করে দিয়েছে, কারণ এটা প্র‍্যাকটিক্যাল লাইফে অনেক বেশি ব্যবহৃত হয়। তুমি চাইলে এটাকে এমনভাবে ভাবতে পারো—যেন একটা state ভ্যারিয়েবল আছে, কিন্তু তার কোনো setter নেই।

যদি তুমি Object-Oriented Programming জানো, তাহলে ref-কে ক্লাসের instance variable-এর মতো মনে হতে পারে—শুধু এখানে this.something না লিখে somethingRef.current লেখো।


নিচের অংশটি বাংলায় অনুবাদ করা হলো:


কখন ref ব্যবহার করা উচিত

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

তোমার কম্পোনেন্ট যদি কোনো মান সংরক্ষণ করতে চায়, কিন্তু সেটা UI রেন্ডারে প্রভাব ফেলে না, তাহলে ref ব্যবহার করো।


ref ব্যবহারের সেরা কৌশলগুলো

নিচের নিয়মগুলো মানলে তোমার কম্পোনেন্ট আরও predictable (অনুমানযোগ্য) হবে:

  • ref-কে escape hatch (শেষ উপায়) হিসেবে বিবেচনা করো। যখন তুমি বাইরের সিস্টেম বা ব্রাউজার API-এর সাথে কাজ করো, তখন ref দরকার হয়। তবে, যদি তোমার অ্যাপের অনেক লজিক বা ডেটা ফ্লো ref-এর ওপর নির্ভর করে, তাহলে তোমার অ্যাপ ডিজাইন নতুন করে ভাবা উচিত।

  • রেন্ডারের সময় ref.current পড়া বা লেখা উচিত না। যদি কোনো তথ্য রেন্ডারের সময় দরকার হয়, তাহলে সেটা state দিয়ে রাখো। কারণ React জানে না ref.current কবে বদলাবে, তাই রেন্ডারের সময় এটা পড়লে তোমার কম্পোনেন্টের আচরণ অনির্ভরযোগ্য হয়ে যেতে পারে। (একটা ব্যতিক্রম হলো এরকম কোড: if (!ref.current) ref.current = new Thing() — এটা শুধু প্রথম রেন্ডারে একবার সেট করে।)


React-এর স্টেটের যেসব সীমাবদ্ধতা আছে, সেগুলো ref-এ নেই। যেমন: state প্রতি রেন্ডারে একটা snapshot (opens in a new tab) হয় এবং sync ভাবে আপডেট হয় না (opens in a new tab)। কিন্তু ref.current বদলালে সঙ্গে সঙ্গেই সেটা বদলে যায়:

ref.current = 5;
console.log(ref.current); // 5

এটা সম্ভব কারণ ref আসলে একটা সাধারণ জাভাস্ক্রিপ্ট অবজেক্ট, তাই ওটা জাভাস্ক্রিপ্ট অবজেক্টের মতোই আচরণ করে।

ref-এর সাথে কাজ করার সময় mutation এড়ানো (opens in a new tab) নিয়ে চিন্তা করার দরকার নেই। যতক্ষণ না সেই অবজেক্ট রেন্ডারের কাজে ব্যবহার হচ্ছে, React কোনো সমস্যাই করে না।


ref ও DOM

তুমি ref দিয়ে যেকোনো মানের রেফারেন্স রাখতে পারো। তবে ref সবচেয়ে বেশি ব্যবহার করা হয় DOM element এক্সেস করতে। যেমন, কোনো ইনপুট ফিল্ড প্রোগ্রামেটিকালি ফোকাস করতে চাইলে ref খুবই কাজে লাগে। যখন তুমি JSX-এ <div ref={myRef}> এর মতো করে ref দাও, তখন React ওই DOM এলিমেন্টটিকে myRef.current-এ রেখে দেয়। আর DOM থেকে যদি এলিমেন্টটি মুছে যায়, তাহলে React myRef.current-কে null করে দেয়। এই বিষয়ে বিস্তারিত জানতে পারো: Manipulating the DOM with Refs

(পুনরালোচনা)

  • Ref হলো এমন একটি উপায় যা দিয়ে এমন ভ্যালু ধরে রাখা যায় যেটা রেন্ডারিং-এ ব্যবহৃত হয় না। তুমি এটা খুব বেশি ব্যবহার করতে হবে না।
  • Ref আসলে একটি সাধারণ জাভাস্ক্রিপ্ট অবজেক্ট, যার একটি মাত্র প্রপার্টি থাকে — current, যেটা তুমি পড়তেও পারো আবার সেটও করতে পারো।
  • React-কে useRef হুক কল করে তুমি একটা ref দিতে বলো।
  • State এর মতোই, ref ও component রি-রেন্ডার হওয়ার পরেও তথ্য ধরে রাখতে পারে।
  • তবে state এর থেকে আলাদা ভাবে, ref এর current প্রপার্টি সেট করলেও component রি-রেন্ডার হয় না।
  • ref.current কে রেন্ডারিং এর সময় পড়া বা লেখা উচিত নয়, এতে component এর আচরণ অনিশ্চিত হতে পারে।