React এর হাতেখড়ি (পর্ব ১৭) — useRef ও Custom Hook পরিচিতি শিখুন

useRef ও Custom Hook পরিচিতি

আগের পর্বে React-এ Styling শিখেছিলাম। এখন শুরু হচ্ছে "Advanced Hooks ও Organization" — এই সিরিজের শেষের আগের পর্ব। আজ দুটো টপিক দেখব — useRef (DOM এলিমেন্টের সরাসরি রেফারেন্স নেওয়া) আর Custom Hook (নিজের রিইউজেবল Hook বানানো)।

useRef কী?

useRef একটা "কন্টেইনার" বানায় যেটা কোনো মান ধরে রাখতে পারে, কিন্তু useState-এর মতো পাল্টালে re-render ঘটায় না। সবচেয়ে সাধারণ ব্যবহার — DOM এলিমেন্টের সরাসরি রেফারেন্স রাখা, ঠিক Advanced JS-এ যেমন document.getElementById() ব্যবহার করতাম।

import { useRef } from "react";

function FocusInput() {
  const inputRef = useRef(null); // প্রাথমিক মান null

  function handleClick() {
    inputRef.current.focus(); // সরাসরি DOM এলিমেন্ট অ্যাক্সেস
  }

  return (
    <div>
      <input ref={inputRef} />
      <button onClick={handleClick}>ইনপুটে ফোকাস করো</button>
    </div>
  );
}

ref={inputRef} দিয়ে React এই <input>-এর আসল DOM এলিমেন্টটা inputRef.current-এ বসিয়ে দেয় — তারপর .focus(), .scrollIntoView()-এর মতো যেকোনো vanilla DOM মেথড সরাসরি কল করা যায়।

useState বনাম useRef — মূল পার্থক্য

বৈশিষ্ট্য useState useRef
মান পাল্টালে re-render হ্যাঁ না
মান অ্যাক্সেস করা সরাসরি ভ্যারিয়েবল .current দিয়ে
কখন ব্যবহার করবে UI-তে যা দেখাতে হবে DOM রেফারেন্স, বা UI-তে দেখানো লাগে না এমন মান

useRef-এর আরেকটা ব্যবহার — Render-এর মধ্যে মান "মনে রাখা"

কখনো কখনো এমন কিছু ট্র্যাক করতে হয় যেটা UI-তে সরাসরি দেখানো লাগে না, কিন্তু re-render-এও হারিয়ে যাওয়া উচিত না — যেমন কতবার একটা ফাংশন কল হয়েছে সেই কাউন্ট (UI-তে না দেখিয়ে)।

function ClickTracker() {
  const clickCountRef = useRef(0);

  function handleClick() {
    clickCountRef.current += 1; // পাল্টালেও re-render হবে না
    console.log("মোট ক্লিক:", clickCountRef.current);
  }

  return <button onClick={handleClick}>চাপো (console দেখো)</button>;
}

সতর্কতা: যদি এই কাউন্ট স্ক্রিনে দেখানোর দরকার হয়, useRef ভুল পছন্দ — কারণ মান পাল্টালেও UI আপডেট হবে না। UI-তে দেখানোর যেকোনো ডেটার জন্য সবসময় useState ব্যবহার করো।

লাইভ ডেমো — useRef দিয়ে ইনপুট ফোকাস করা

এখন Custom Hook — সমস্যাটা কী?

একটা প্যাটার্ন যদি একাধিক component-এ বারবার লাগে, সেই লজিক প্রতিবার নতুন করে লেখা বিরক্তিকর। যেমন — Advanced JS পর্ব ১-এর localStorage সেভ করার লজিক যদি একাধিক component-এ দরকার হয়:

// ❌ একই লজিক দুই জায়গায় কপি-পেস্ট করতে হচ্ছে
function ThemeToggle() {
  const [theme, setTheme] = React.useState(
    () => localStorage.getItem("theme") || "light"
  );

  useEffect(() => {
    localStorage.setItem("theme", theme);
  }, [theme]);
  // ...
}

function LanguageSelector() {
  const [language, setLanguage] = React.useState(
    () => localStorage.getItem("language") || "bn"
  );

  useEffect(() => {
    localStorage.setItem("language", language);
  }, [language]);
  // ... প্রায় হুবহু একই কোড আবার!
}

Custom Hook দিয়ে সমাধান

Custom Hook হলো একটা সাধারণ JavaScript ফাংশন, যার নাম use দিয়ে শুরু হয়, আর যেটা ভেতরে React-এর built-in Hook (useState, useEffect) ব্যবহার করে। এতে পুনরাবৃত্ত লজিক একটা জায়গায় গুটিয়ে ফেলা যায়।

// useLocalStorage.js — একটা কাস্টম Hook
function useLocalStorage(key, defaultValue) {
  const [value, setValue] = React.useState(
    () => localStorage.getItem(key) || defaultValue
  );

  React.useEffect(() => {
    localStorage.setItem(key, value);
  }, [key, value]);

  return [value, setValue]; // useState-এর মতোই একটা অ্যারে রিটার্ন করে
}

এখন ব্যবহার করা যায় ঠিক useState-এর মতোই, কিন্তু localStorage সিঙ্ক স্বয়ংক্রিয়ভাবে হয়ে যায়:

function ThemeToggle() {
  const [theme, setTheme] = useLocalStorage("theme", "light");
  // এই একটা লাইনেই localStorage সিঙ্ক পাওয়া গেল!

  return (
    <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
      বর্তমান থিম: {theme}
    </button>
  );
}

function LanguageSelector() {
  const [language, setLanguage] = useLocalStorage("language", "bn");
  // একই Hook, ভিন্ন key — সম্পূর্ণ আলাদা স্বাধীন state
}

দুইটা component-এ একই useLocalStorage ব্যবহার হলেও, প্রতিটা কল সম্পূর্ণ স্বাধীন state তৈরি করছে — ঠিক যেমন একই useState একাধিকবার কল করলে হয়।

Custom Hook-এর নিয়ম

  • নাম অবশ্যই use দিয়ে শুরু হতে হবে (যেমন useLocalStorage) — এতে React আর অন্য ডেভেলপাররা বুঝতে পারে এটা একটা Hook
  • এর ভেতরে অন্য Hook (useState, useEffect) কল করতে পারে
  • যেকোনো কিছু রিটার্ন করতে পারে — অ্যারে, অবজেক্ট, একটা মান, বা কিছুই না

লাইভ ডেমো — Custom Hook একাধিক জায়গায় ব্যবহার

নিচে useLocalStorage দুইবার ব্যবহার হচ্ছে — একটা নাম সেভ করার জন্য, একটা থিম বাছাইয়ের জন্য। পেজ রিফ্রেশ করলেও দুটোই থেকে যাবে।

দুইটা field-ই একই useLocalStorage Hook ব্যবহার করছে, ভিন্ন key দিয়ে।

আরেকটা উদাহরণ — useToggle

ছোট, সহজ Custom Hook-ও খুব উপকারী — বুলিয়ান টগল করার একটা সাধারণ প্যাটার্ন।

function useToggle(initialValue = false) {
  const [value, setValue] = React.useState(initialValue);

  function toggle() {
    setValue((prev) => !prev); // পর্ব ৫-এর functional setState প্যাটার্ন
  }

  return [value, toggle];
}

// ব্যবহার
function Modal() {
  const [isOpen, toggleOpen] = useToggle(false);

  return (
    <div>
      <button onClick={toggleOpen}>{isOpen ? "বন্ধ করো" : "খুলো"}</button>
      {isOpen && <div>মডালের কন্টেন্ট</div>}
    </div>
  );
}

নিজে চেষ্টা করো

Exercise:

  1. উপরের FocusDemo নিজে বানাও, তারপর একটা ফর্ম বানাও যেটা mount হওয়ার সাথে সাথে স্বয়ংক্রিয়ভাবে প্রথম ইনপুটে ফোকাস করে (হিন্ট: useEffect-এ ref ব্যবহার করো)।
  2. উপরের useLocalStorage নিজে বানাও, তোমার নিজের একটা প্রজেক্টে ব্যবহার করো।
  3. উপরের useToggle নিজে বানাও, একটা "শো/হাইড পাসওয়ার্ড" ফিচারে ব্যবহার করো।
  4. একটা useWindowWidth Custom Hook বানাও (পর্ব ১২-এর WindowWidthTracker উদাহরণ থেকে ধারণা নিয়ে)।

পরের পর্বে যা থাকছে

এটাই "React এর হাতেখড়ি" সিরিজের শেষ থিওরি পর্ব। পরের পর্বে (সিরিজের শেষ পর্ব, পর্ব ১৮) আমরা এই পুরো সিরিজে শেখা সবকিছু — useState, useEffect, props, Conditional Rendering, List Rendering, Custom Hook — মিলিয়ে একটা সম্পূর্ণ Mini Project বানাব।

একটি মন্তব্য পোস্ট করুন

নবীনতর পূর্বতন

যোগাযোগ ফর্ম