আগের পর্বে 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:
- উপরের
FocusDemoনিজে বানাও, তারপর একটা ফর্ম বানাও যেটা mount হওয়ার সাথে সাথে স্বয়ংক্রিয়ভাবে প্রথম ইনপুটে ফোকাস করে (হিন্ট: useEffect-এ ref ব্যবহার করো)। - উপরের
useLocalStorageনিজে বানাও, তোমার নিজের একটা প্রজেক্টে ব্যবহার করো। - উপরের
useToggleনিজে বানাও, একটা "শো/হাইড পাসওয়ার্ড" ফিচারে ব্যবহার করো। - একটা
useWindowWidthCustom Hook বানাও (পর্ব ১২-এরWindowWidthTrackerউদাহরণ থেকে ধারণা নিয়ে)।
পরের পর্বে যা থাকছে
এটাই "React এর হাতেখড়ি" সিরিজের শেষ থিওরি পর্ব। পরের পর্বে (সিরিজের শেষ পর্ব, পর্ব ১৮) আমরা এই পুরো সিরিজে শেখা সবকিছু — useState, useEffect, props, Conditional Rendering, List Rendering, Custom Hook — মিলিয়ে একটা সম্পূর্ণ Mini Project বানাব।