React এর হাতেখড়ি সিরিজে আমরা ডেটা আনতে useEffect ব্যবহার করতাম — কম্পোনেন্ট মাউন্ট হওয়ার পর, ব্রাউজারে fetch করে, state আপডেট করে। পর্ব ৩-এ শেখা Server Component-এর কারণে Next.js-এ এটা সম্পূর্ণ ভিন্নভাবে করা যায় — সরাসরি, সাধারণ async/await দিয়ে, কোনো useEffect ছাড়াই।
পুরোনো প্যাটার্ন বনাম নতুন প্যাটার্ন
// ❌ React এর হাতেখড়ি সিরিজের পুরোনো প্যাটার্ন (Client Component-এ প্রয়োজন হলে এখনো ব্যবহার হয়)
"use client";
function BookList() {
const [books, setBooks] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetch("https://my-first-app-default-rtdb.firebasedatabase.app/books.json")
.then((res) => res.json())
.then((data) => { setBooks(data); setIsLoading(false); });
}, []);
// ...
}
// ✅ Next.js Server Component-এ নতুন প্যাটার্ন — অনেক সহজ
async function BookList() {
const res = await fetch("https://my-first-app-default-rtdb.firebasedatabase.app/books.json");
const books = await res.json();
return (
<ul>
{Object.values(books || {}).map((book, i) => (
<li key={i}>{book.title}</li>
))}
</ul>
);
}
লক্ষ্য করুন — কম্পোনেন্ট ফাংশনটাই async! কোনো isLoading state, কোনো useEffect লাগছে না — কারণ এই কোড সার্ভারে চলে, যেখানে সরাসরি await করা সম্পূর্ণ স্বাভাবিক (Node.js-এর যেকোনো ব্যাকএন্ড কোডের মতোই)।
Next.js-এর বিল্ট-ইন Caching
Next.js-এ fetch() সাধারণ ব্রাউজার fetch-এর চেয়ে বেশি কাজ করে — Cloudflare এর হাতেখড়ি পর্ব ১১-এর Cache-Control-এর মতোই একটা ধারণা, কিন্তু বিল্ট-ইন। ডিফল্টভাবে Next.js একবার ফেচ করা রেসপন্স ক্যাশ করে রাখে।
// ডিফল্ট — সবসময় ক্যাশ থেকে (Static)
const res = await fetch("https://.../books.json");
// প্রতিবার নতুন করে আনা — কখনো ক্যাশ না করা (Dynamic)
const res = await fetch("https://.../books.json", { cache: "no-store" });
// নির্দিষ্ট সময় পর পর রিফ্রেশ — সেকেন্ডে (ISR-এর মতো ধারণা)
const res = await fetch("https://.../books.json", {
next: { revalidate: 60 }, // ৬০ সেকেন্ড পর পর নতুন ডেটা
});
এই তিনটা অপশনই পরের পর্বে (Static vs Dynamic Rendering) আরও গভীরে যাব — কিন্তু এখানে মূল ধারণা হলো, প্রতিটা fetch() কলে আলাদাভাবে ঠিক করা যায় ডেটা কতটা "তাজা" হওয়া দরকার।
একাধিক ফেচ — সমান্তরালে
একাধিক await একটার পর একটা লিখলে সেগুলো ধারাবাহিকভাবে (sequentially) চলে — ধীর। Advanced JS-এর Promise.all-এর মতোই প্যাটার্ন ব্যবহার করে সমান্তরালে (parallel) আনা যায়।
// ❌ ধীর — একটার পর একটা অপেক্ষা করছে
async function Dashboard() {
const booksRes = await fetch("https://.../books.json");
const books = await booksRes.json();
const usersRes = await fetch("https://.../users.json"); // books শেষ না হওয়া পর্যন্ত শুরুই হয়নি
const users = await usersRes.json();
return <div>{/* ... */}</div>;
}
// ✅ দ্রুত — দুটো একসাথে শুরু হয়
async function Dashboard() {
const [booksRes, usersRes] = await Promise.all([
fetch("https://.../books.json"),
fetch("https://.../users.json"),
]);
const books = await booksRes.json();
const users = await usersRes.json();
return <div>{/* ... */}</div>;
}
Firebase RTDB থেকে ডেটা আনা — বাস্তব উদাহরণ
Firebase এর হাতেখড়ি পর্ব ৭-এর query, orderByChild REST API দিয়েও কাজ করে, কারণ RTDB আসলে একটা HTTP endpoint (.json-এ শেষ হওয়া URL, Cloudflare এর হাতেখড়ি পর্ব ৬-এর মতোই)।
// app/books/page.js
async function getBooks() {
const res = await fetch(
"https://my-first-app-default-rtdb.firebasedatabase.app/books.json",
{ next: { revalidate: 300 } } // ৫ মিনিট পর পর রিফ্রেশ
);
if (!res.ok) {
throw new Error("বই লোড করা যায়নি");
}
return res.json();
}
export default async function BooksPage() {
const books = await getBooks();
return (
<div>
<h1>বইয়ের তালিকা</h1>
<ul>
{Object.entries(books || {}).map(([id, book]) => (
<li key={id}>{book.title} — {book.author}</li>
))}
</ul>
</div>
);
}
Loading State — এখনো দরকার, কিন্তু ভিন্নভাবে
Server Component-এ isLoading state ম্যানুয়ালি লেখার দরকার নেই — Next.js-এর নিজস্ব loading.js ফাইল দিয়ে এটা হ্যান্ডেল হয়, যা আমরা পরের পর্বে বিস্তারিত দেখব। আপাতত জেনে রাখা যথেষ্ট যে fetch চলাকালীন কী দেখানো হবে তা আলাদা একটা ফাইলে ঠিক করা হয়, কম্পোনেন্টের ভেতরে state দিয়ে না।
ইন্টারেক্টিভ — Cache Option বেছে নাও
নিচে বিভিন্ন পরিস্থিতিতে কোন fetch অপশন মানানসই তা দেখো।
ভালো অভ্যাস
- একাধিক স্বাধীন fetch সমান্তরালে চালান —
Promise.allদিয়ে, লোড টাইম উল্লেখযোগ্যভাবে কমে - সবসময় ভাবুন ডেটা কতটা "তাজা" হওয়া দরকার — সবকিছুতে
no-storeদিলে ক্যাশিং-এর সুবিধাই হারিয়ে যায় - Client Component-এ এখনো useEffect লাগতে পারে — শুধু ইউজারের ইন্টারঅ্যাকশনের ওপর নির্ভর করে ডেটা আনতে হলে (যেমন সার্চ রেজাল্ট), Server Component-এর initial fetch যথেষ্ট না
নিজে চেষ্টা করো
Exercise:
- নিজের Firebase RTDB (বা কোনো পাবলিক টেস্ট API) থেকে একটা Server Component পেজে সরাসরি
fetchদিয়ে ডেটা এনে দেখাও। - একই পেজে দুটো ভিন্ন উৎস থেকে
Promise.allদিয়ে সমান্তরালে ডেটা আনো। next: { revalidate: 10 }দিয়ে একটা fetch টেস্ট করো — ১০ সেকেন্ড পর পেজ রিফ্রেশ করে দেখো ডেটা বদলাচ্ছে কিনা।- ইচ্ছাকৃতভাবে একটা ভুল URL দিয়ে fetch করো,
try/catchদিয়ে এরর হ্যান্ডেল করো।
পরের পর্বে যা থাকছে
পরের পর্বে আমরা শিখব Static vs Dynamic Rendering — SSG, SSR, ISR-এর পার্থক্য, কোন পেজ বিল্ড টাইমে জেনারেট হবে, কোনটা রিকোয়েস্ট টাইমে, আর কীভাবে সঠিক কৌশল বেছে নিতে হয়।