অবশ্যই, নিচে আমি খুবই সহজ ভাষায় ও সরল বাক্য ব্যবহার করে অনুবাদ করলাম, যেন আপনার পাঠকরা (বিশেষ করে যারা বাংলা মাধ্যমে React শিখছেন) সহজেই বুঝতে পারে:
practice api: https://api.github.com/search/users?q=sabnur (opens in a new tab)
ডেটা লোড করা (Fetching Data)
অনেক অ্যাপে ডেটা লোড করতে useEffect
ব্যবহার করা হয়। সাধারণত আমরা নিচের মতো কোড লিখি:
function SearchResults({ query }) {
const [results, setResults] = useState([]);
const [page, setPage] = useState(1);
useEffect(() => {
// 🔴 ভুল: শুধু ফেচ করে ছেড়ে দেওয়া, কোনো কনট্রোল নেই
fetchResults(query, page).then((json) => {
setResults(json);
});
}, [query, page]);
function handleNextPageClick() {
setPage(page + 1);
}
}
এই ফেচ (লোড) করার কোডটা ইভেন্ট হ্যান্ডলারে দেওয়ার দরকার নেই।
অনেকে ভাবে, যেহেতু আগের উদাহরণগুলোতে ইভেন্টে (যেমন: ক্লিক, টাইপ) ডেটা লোড করছিলাম, এখানেও তাই করতে হবে।
কিন্তু এখানে বিষয়টা আলাদা। কারণ সার্চ বক্সে query
অনেক সময় URL থেকে আসে, আবার ইউজার শুধু Back/Forward বাটন চাপলেও query
বদলাতে পারে — টাইপ না করেও।
তাই, যখন query
বা page
বদলায়, তখন সাথে সাথে নতুন ডেটা লোড হওয়া দরকার। এজন্য useEffect
ব্যবহার করা হয়েছে।
❗ কিন্তু উপরের কোডে একটি সমস্যা আছে:
ধরুন, আপনি খুব দ্রুত "hello"
টাইপ করলেন।
তখন একসাথে "h"
, "he"
, "hel"
, "hell"
, "hello"
— এই পাঁচটি রিকোয়েস্ট যাবে।
এখন যদি "hell"
এর রেজাল্ট "hello"
এর পরে আসে, তাহলে সেই পুরনো রেজাল্টটা স্ক্রিনে দেখাবে।
এর মানে ভুল রেজাল্ট দেখা যাবে।
এটা বলে race condition — মানে, একাধিক রিকোয়েস্ট একসাথে দৌড় দিয়েছে, কে আগে আসবে নিশ্চিত না।
✅ সমাধান: পুরনো রেসপন্স যেন ইগনোর হয়, তাই আমরা একটা "ক্লিনআপ ফাংশন" ব্যবহার করবো:
function SearchResults({ query }) {
const [results, setResults] = useState([]);
const [page, setPage] = useState(1);
useEffect(() => {
let ignore = false;
fetchResults(query, page).then((json) => {
if (!ignore) {
setResults(json);
}
});
return () => {
ignore = true;
};
}, [query, page]);
function handleNextPageClick() {
setPage(page + 1);
}
}
এই কোড বলছে:
যদি এই Effect
এর পর অন্য কোনো নতুন রিকোয়েস্ট চলে আসে, তাহলে আগেরটাকে ইগনোর করো।
এতে পুরনো রেসপন্স স্ক্রিনে আর দেখাবে না, শুধু নতুনটাই দেখাবে।
আরও কিছু ভাবার বিষয়:
- আগের রেজাল্ট যেন ক্যাশ করা যায় (যাতে Back করলে আগের স্ক্রিন সঙ্গে সঙ্গে আসে),
- সার্ভার থেকেই ডেটা লোড করা যায় (যাতে প্রথমবারেই স্পিনার না ঘোরে),
- চাইল্ড কম্পোনেন্ট নিজের ডেটা লোড করতে পারে প্যারেন্ট না থেমেই।
এই জটিল বিষয়গুলো সব UI লাইব্রেরিতেই হয়, শুধু React-এ নয়। এজন্যই বড় ফ্রেমওয়ার্কগুলোতে ডেটা ফেচ করার জন্য বিল্ট-ইন সাপোর্ট থাকে।
🎯 সহজ করে ফেচ করার জন্য কাস্টম হুক বানানো যায়:
function SearchResults({ query }) {
const [page, setPage] = useState(1);
const params = new URLSearchParams({ query, page });
const results = useData(`/api/search?${params}`);
function handleNextPageClick() {
setPage(page + 1);
}
}
function useData(url) {
const [data, setData] = useState(null);
useEffect(() => {
let ignore = false;
fetch(url)
.then((response) => response.json())
.then((json) => {
if (!ignore) {
setData(json);
}
});
return () => {
ignore = true;
};
}, [url]);
return data;
}
এই useData
হুক এর কাজ হলো:
যেকোনো URL দিলে, সেটা থেকে ডেটা নিয়ে আসবে।
পুরনো রিকোয়েস্ট আসলে ইগনোর করে দেবে।
চাইলে আপনি loading
এবং error
স্টেটও যোগ করতে পারেন।
এই ধরনের হুক নিজে বানাতে পারেন বা React এর প্যাকেজ ব্যবহার করতে পারেন।
🔚 শেষ কথা:
useEffect
ব্যবহার করলে খেয়াল রাখবেন — যদি কোনো কাজ বারবার লাগে, তাহলে সেটা কাস্টম হুক হিসেবে আলাদা করে ফেলা ভালো।
যত কম useEffect
থাকবে, আপনার অ্যাপ তত পরিষ্কার ও মেইনটেইন করা সহজ হবে।
আপনি চাইলে আমি এই অংশটি আপনার বইয়ের ধরনে সাজিয়ে দিতে পারি। আরও সহজ উদাহরণ বা রিয়েল-ওয়ার্ল্ড অ্যাপের অংশ হিসেবে ব্যবহার করতে চাইলে, সেটাও বলে দিন — আমি সেটি করেও দেব।
অসাধারণ প্রশ্ন করেছেন, Md. Mojnu Miah ভাই। এখন আমি এই তিনটি গুরুত্বপূর্ণ কনসেপ্টের প্রতিটা আলাদাভাবে, ধাপে ধাপে ব্যাখ্যা করব — যেন আপনি নিজে নিজে হাতে কলমে প্র্যাকটিস করতে পারেন।
🧠 ১. আগের রেজাল্ট যেন ক্যাশ করা যায় (Caching previous results)
💡 সমস্যা:
একবার সার্চ করে "react"
লিখলেন, ডেটা এলো।
এরপর "vue"
লিখলেন।
আবার "react"
এ ফিরে এলেন — তখন আবার ফেচ হলো।
✅ সমাধান:
আমরা চাই, একবার লোড হওয়া ডেটা ক্যাশে থাকুক।
যখন একই query
আবার আসে, তখন ক্যাশ থেকে দেখাবো।
🛠️ Hands-on স্টেপ:
Step 1: একটা ক্যাশ অবজেক্ট বানান
const cache = {};
Step 2: useDataWithCache
নামে কাস্টম হুক বানান
function useDataWithCache(url) {
const [data, setData] = useState(cache[url] || null);
const [loading, setLoading] = useState(!cache[url]);
useEffect(() => {
if (cache[url]) return; // ক্যাশে থাকলে আবার ফেচ করব না
let ignore = false;
setLoading(true);
fetch(url)
.then((res) => res.json())
.then((json) => {
if (!ignore) {
cache[url] = json; // ক্যাশে রেখে দিলাম
setData(json);
setLoading(false);
}
});
return () => {
ignore = true;
};
}, [url]);
return { data, loading };
}
Step 3: SearchResults
কম্পোনেন্টে হুক ব্যবহার করুন
const params = new URLSearchParams({ query, page });
const { data: results, loading } = useDataWithCache(`/api/search?${params}`);
🧠 ২. সার্ভার থেকেই ডেটা লোড করা (Server-Side Data Fetching)
💡 সমস্যা:
React ক্লায়েন্টে চলে, তাই প্রথমে UI আসে — তারপর ফেচ হয়। ফলে শুরুতে স্পিনার ঘোরে।
✅ সমাধান:
Next.js বা Remix-এর মতো ফ্রেমওয়ার্ক ব্যবহার করে আপনি ডেটা প্রথমেই সার্ভার থেকে ফেচ করতে পারেন। React নিজে এটা করে না, তাই Framework দরকার।
🛠️ Hands-on (Next.js দিয়ে):
Step 1: একটি Next.js পেজ তৈরি করুন
npx create-next-app@latest my-app
Step 2: getServerSideProps
ব্যবহার করে সার্ভার সাইডে ডেটা লোড করুন
// pages/search.js
export async function getServerSideProps(context) {
const query = context.query.query || "";
const res = await fetch(`https://api.example.com/search?q=${query}`);
const results = await res.json();
return { props: { results, query } };
}
export default function SearchPage({ results, query }) {
return (
<div>
<h2>Search results for "{query}"</h2>
<ul>
{results.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
</div>
);
}
এখানে স্পিনার ঘোরে না। পেজ যখনই লোড হয়, তখনই HTML তৈরি হয়ে আসে — ডেটাসহ।
🧠 ৩. চাইল্ড কম্পোনেন্ট নিজের ডেটা লোড করতে পারে, প্যারেন্ট না থেমেই
💡 সমস্যা:
অনেক সময় ডেটা ফেচ parent → child → sub-child এ যায়। ফলে parent না এলে child আসে না। এটা Waterfall Effect।
✅ সমাধান:
প্রতিটি কম্পোনেন্ট নিজে নিজে ফেচ করুক — তাহলে সব একসাথে লোড হবে।
🛠️ Hands-on:
Step 1: Parent.jsx
function Parent() {
return (
<div>
<h1>Dashboard</h1>
<Profile />
<Notifications />
</div>
);
}
Step 2: Profile.jsx (চাইল্ড)
function Profile() {
const { data, loading } = useDataWithCache("/api/profile");
if (loading) return <p>Loading profile...</p>;
return <div>Welcome, {data.name}</div>;
}
Step 3: Notifications.jsx (আরেকটা চাইল্ড)
function Notifications() {
const { data, loading } = useDataWithCache("/api/notifications");
if (loading) return <p>Loading notifications...</p>;
return (
<ul>
{data.map((n) => (
<li key={n.id}>{n.message}</li>
))}
</ul>
);
}
এখানে Parent
শুধু কন্টেইনার —
Profile
ও Notifications
নিজ নিজ ডেটা লোড করছে আলাদাভাবে।
এইভাবেই Waterfall এড়ানো যায়।
📦 উপসংহার (Recap Checklist):
বিষয় | হাতেকলমে প্র্যাকটিস |
---|---|
✅ ক্যাশ | useDataWithCache হুক বানান |
✅ সার্ভার সাইড ফেচ | Next.js ব্যবহার করুন |
✅ চাইল্ড নিজে ফেচ করবে | প্রতিটি কম্পোনেন্টে useDataWithCache ব্যবহার করুন |
আপনি চাইলে আমি এগুলো নিয়ে একটা mini project বানিয়ে দিতে পারি। যেমন "Search with cache", বা "Dashboard with SSR + independent child loading".
বললে আমি সেটাও করে দেব। ✅ আরেকটা কথা — এগুলো আপনার React Bangla বইয়ে Feature Guide বা Advanced Section হিসেবে থাকলে নতুনদের দারুণ কাজে দেবে।
দারুণ! এখন আমি আপনাকে একটি রিয়েল ওয়ার্ল্ড অ্যাপ্লিকেশন আইডিয়া এবং তার পুরো ফোল্ডার স্ট্রাকচার + ফিচার + ইমপ্লিমেন্টেশন গাইডলাইন দেব, যেখানে আপনি নিচের ৩টি জ্ঞান একসাথে ব্যবহার করতে পারবেন:
- ✅ Caching
- ✅ Server-side data fetching
- ✅ Child component independently data fetch
📱 অ্যাপ্লিকেশনের নাম: DevQuiz Hub
🎯 উদ্দেশ্য:
একটি কুইজ অ্যাপ, যেখানে ইউজার প্রোগ্রামিং টপিক (যেমন React, JavaScript) সিলেক্ট করলে কুইজগুলো ফেচ হয় এবং ইউজার উত্তর দিতে পারে।
🔍 আপনি যেসব জ্ঞান ইমপ্লিমেন্ট করবেন:
কনসেপ্ট | অ্যাপে কোথায় লাগবে |
---|---|
✅ Caching | একই টপিক বারবার খুললে আগের কুইজ ক্যাশ থেকে দেখাবো |
✅ Server-side fetching | প্রথমবারেই কুইজ টপিকসহ কনটেন্ট লোড হয়ে যাবে (Next.js) |
✅ Child fetch | প্রতিটি কুইজ কার্ড (চাইল্ড) আলাদাভাবে বিস্তারিত ফেচ করবে |
🗂️ ফোল্ডার স্ট্রাকচার (Next.js + Custom Hooks)
devquiz-hub/
│
├── pages/
│ ├── index.js → কুইজ টপিক লিস্ট (SSR)
│ └── quiz/[topic].js → নির্দিষ্ট টপিকের কুইজ (Client fetch + caching)
│
├── components/
│ ├── QuizCard.js → প্রতিটি কুইজ কার্ড (Child fetches explanation)
│ ├── TopicList.js → টপিক গুলোর লিস্ট
│
├── hooks/
│ └── useDataWithCache.js → ক্যাশ সহ ফেচ করার হুক
│
├── utils/
│ └── cache.js → Simple in-memory cache object
│
└── public/
└── dummy quiz json
🔧 Step-by-Step Feature Implementation
✅ Step 1: Topic লিস্ট দেখানো (Server-Side Rendering)
pages/index.js
export async function getServerSideProps() {
const res = await fetch("https://example.com/api/topics");
const topics = await res.json();
return { props: { topics } };
}
export default function Home({ topics }) {
return (
<div>
<h1>📚 Quiz Topics</h1>
<ul>
{topics.map((topic) => (
<li key={topic.id}>
<a href={`/quiz/${topic.slug}`}>{topic.title}</a>
</li>
))}
</ul>
</div>
);
}
✅ Step 2: নির্দিষ্ট টপিক অনুযায়ী কুইজ দেখানো (Client-side with Caching)
pages/quiz/[topic].js
import { useRouter } from "next/router";
import useDataWithCache from "@/hooks/useDataWithCache";
import QuizCard from "@/components/QuizCard";
export default function TopicQuizPage() {
const router = useRouter();
const { topic } = router.query;
const { data: quizzes, loading } = useDataWithCache(
`/api/quiz?topic=${topic}`
);
if (loading) return <p>Loading quizzes...</p>;
return (
<div>
<h2>📝 Quiz on: {topic}</h2>
{quizzes.map((q) => (
<QuizCard key={q.id} quiz={q} />
))}
</div>
);
}
✅ Step 3: চাইল্ড কম্পোনেন্ট থেকে explanation ফেচ করা
components/QuizCard.js
import useDataWithCache from "@/hooks/useDataWithCache";
export default function QuizCard({ quiz }) {
const { data: explanation, loading } = useDataWithCache(
`/api/explanation?id=${quiz.id}`
);
return (
<div className="card">
<p>
<strong>Q:</strong> {quiz.question}
</p>
<ul>
{quiz.options.map((opt) => (
<li key={opt}>{opt}</li>
))}
</ul>
{loading ? (
<p>Loading explanation...</p>
) : (
<p>
<strong>Explain:</strong> {explanation?.text}
</p>
)}
</div>
);
}
✅ Step 4: Custom Hook with Cache
hooks/useDataWithCache.js
import { useState, useEffect } from "react";
import { cache } from "@/utils/cache";
export default function useDataWithCache(url) {
const [data, setData] = useState(cache[url] || null);
const [loading, setLoading] = useState(!cache[url]);
useEffect(() => {
if (cache[url]) return;
let ignore = false;
fetch(url)
.then((res) => res.json())
.then((json) => {
if (!ignore) {
cache[url] = json;
setData(json);
setLoading(false);
}
});
return () => {
ignore = true;
};
}, [url]);
return { data, loading };
}
utils/cache.js
export const cache = {};
🔥 Bonus Idea: Offline Support or Quiz History Page (Later Feature)
🧪 Hands-on GitHub Demo চাই?
বললে আমি আপনার জন্য এই কোডগুলোর একটি ready-to-run GitHub project বানিয়ে দিতে পারি — আপনার YouTube বা React Bangla বইয়ে এটা use case হিসেবে কাজ করবে।
✅ শেষ কথা:
এই DevQuiz Hub
অ্যাপ আপনার ৩টি কনসেপ্ট ভালোভাবে বোঝাবে:
- React-এ
useEffect
দিয়ে ডেটা ফেচ কিভাবে করে - কাস্টম হুক দিয়ে কনসিস্টেন্ট ফেচ ও ক্যাশিং
- Server-side rendering কীভাবে ডেটা লোড ফাস্ট করে
- চাইল্ড কম্পোনেন্ট কিভাবে নিজে নিজে async data ফেচ করতে পারে
আপনি যদি বলেন, আমি এই প্রজেক্ট GitHub repo বানিয়ে, ভিডিও সহ বানিয়ে দিতে পারি। চাইলে Next.js boilerplate ফাইলও শেয়ার করতে পারি। আপনি কি চান আমি GitHub টেমপ্লেট তৈরি করে দিই?