⏳ React Suspense

🧑 React Suspense কি, কেন আসলো (বিস্তারিত)

ধরুন, আপনি একজন React ডেভেলপার। ইউজার ইন্টারফেস বানাচ্ছেন আর আপনার দরকার—কোনো একটা কম্পোনেন্ট তখনই দেখানো হবে, যখন ডেটা রেডি হবে।

এইরকম সময়ে, আগের মতো useState আর useEffect দিয়ে লোডিং ম্যানেজ করা যায় ঠিকই—but ঝামেলা হয় যখন অনেকগুলো জায়গায় একসাথে লোডিং/এরর হ্যান্ডেল করতে হয়।

React Suspense আসছে এই সমস্যার সমাধানে।


🔍 Suspense কি?

<Suspense> আসলে একটা React কম্পোনেন্ট, যেটা অন্য কোনো কম্পোনেন্টকে “ধরে” রাখে যতক্ষণ না সেটা পুরো রেন্ডার করার জন্য প্রস্তুত।

📦 সহজ ভাষায়:

<Suspense fallback={<Loading />}>
  <Albums />
</Suspense>

এই কোডে, যদি Albums কম্পোনেন্টের ভেতরে ডেটা ফেচ করতে গিয়ে "সময় লাগে", তখন Suspense Loading দেখাবে।


🤔 Suspense কিভাবে কাজ করে?

ধরেন একটা কম্পোনেন্ট রেন্ডার হচ্ছে, হঠাৎ তার মধ্যে ডেটা দরকার পড়লো, আর সেই ডেটা আসা পর্যন্ত অপেক্ষা করতে হবে।

তখন কী হয়?

  1. প্রথমত ঐ কম্পোনেন্টটি মূল JSX Return করবে না, একটা Promise থ্রো করে
  2. React দেখে, “ওহ! Suspense আছে কাছেই?” — যদি থাকে, সেটা fallback UI (যেমন Loading spinner) দেখাতে শুরু করে। যতক্ষণ না Promise রেজলভ হয়।
  3. Promise resolve হলেই আসল কম্পোনেন্ট (JSX) রেন্ডার হয়।

Implmentation Suspense (চলুন বাস্তব উদাহরণ দেখি)

Option 1: wrapPromise() ম্যানুয়ালি লিখে ফেলো

React এ use() হুক আসার আগে Suspense ব্যবহার করতে চাইলে আমাদের wrapperPromise বা resource.read() ধরনের ম্যানুয়াল Promise handling utility লিখতে হতো। এটা বোঝানো দরকার, যেন পাঠক বুঝতে পারেন কিভাবে Suspense এর ধারণা এসেছে এবং আগেও এটা কিভাবে কাজ করত।

আগে আমরা wrapPromise নিয়ে আলোচনা করব, তারপর দেখাব কিভাবে use() এসে সব সহজ করে দিলো। সেইসাথে বোঝানো হবে এই পরিবর্তন কেন গুরুত্বপূর্ণ।


ধরুন, আপনার একটা কম্পোনেন্ট আছে, যেটা API থেকে ডেটা আনবে। আপনি চান, যতক্ষণ না ডেটা আসে, ততক্ষণ একটা “⏳ Loading…” দেখা যাবে।

আগে (মানে <Suspense/> আসার আগ পর্যন্ত) আপনি কীভাবে করতেন?

⚙️ useState + useEffect

const [albums, setAlbums] = useState(null);
 
useEffect(() => {
  fetch("/albums")
    .then((res) => res.json())
    .then((data) => setAlbums(data));
}, []);

এর সাথে একটা isLoading, একটা error state রাখতে হতো। এটা ছোট অ্যাপে চলত, কিন্তু জটিল অ্যাপে কোড হতো বিশ্রী।


🔁 তখন React Suspense আসলো— কিন্তু একটি সমস্যা

React তখন বললো:

“তুমি চাইলে এখন কম্পোনেন্টকে রেন্ডার হতে থামিয়ে দিতে পারো, যতক্ষণ না ডেটা আসে।”


function wrapPromise(promise) {
  let status = "pending";
  let result;
 
  const suspender = promise.then(
    (res) => {
      status = "success";
      result = res;
    },
    (err) => {
      status = "error";
      result = err;
    }
  );
 
  return {
    read() {
      if (status === "pending") {
        throw suspender;
      } else if (status === "error") {
        throw result;
      } else {
        return result;
      }
    },
  };
}

🎯 কোডে ব্যবহার করি এখন

// data.js
function fetchAlbums() {
  return fetch("/albums").then((res) => res.json());
}
const albumResource = wrapPromise(fetchAlbums());
// Albums.js
function Albums() {
  const albums = albumResource.read(); // এখানে Suspense কাজ করে
  return (
    <ul>
      {albums.map((a) => (
        <li key={a.id}>{a.title}</li>
      ))}
    </ul>
  );
}
<Suspense fallback={<Loading />}>
    <Albums />
</Suspense>

❗ wrapperPromise() সমস্যা কী?

  • wrapPromise() নিজে লিখতে হতো
  • কনসিস্টেন্ট error boundary হ্যান্ডলিং কঠিন ছিল
  • Boilerplate অনেক

🎯 React 18 থেকে এটা অনেক সহজ হয়েছে কারণ এখন use() নামের হুক আছে।


Option 2: use() হুক ব্যবহার করে

১. Data fetch করার ফাংশন

// data.js
let cache = new Map();
 
export function fetchData(url) {
  if (!cache.has(url)) {
    cache.set(url, getData(url));
  }
  return cache.get(url);
}
 
async function getData(url) {
  await new Promise((resolve) => setTimeout(resolve, 2000)); // দেরি করে
  const response = await fetch(url);
  return await response.json();
}

২. Albums কম্পোনেন্ট (Data fetch করে)

// Albums.js
import { use } from "react";
import { fetchData } from "./data.js";
 
export default function Albums() {
  const albums = use(fetchData("https://jsonplaceholder.typicode.com/albums"));
  return (
    <ul>
      {albums.slice(0, 5).map((album) => (
        <li key={album.id}>{album.title}</li>
      ))}
    </ul>
  );
}

⚠️ এখানে কিন্তু useEffect, useState কিছুই নাই! শুধুই use() হুক এবং এটা নিজে বুঝে নিচ্ছে কখন fallback দেখাবে।


৩. App.js - Suspense ব্যবহার করে

// App.js
import { Suspense } from "react";
import Albums from "./Albums.js";
 
function Loading() {
  return <h2>⏳ Loading Albums...</h2>;
}
 
export default function App() {
  return (
    <div>
      <h1>My Music App</h1>
      <Suspense fallback={<Loading />}>
        <Albums />
      </Suspense>
    </div>
  );
}

🧠 এখন যদি Albums ফেচিংয়ে সময় নেয়, তখন Loading দেখাবে, আর ডেটা চলে আসলে Albums রেন্ডার হবে।


⛓️ যদি কিছু Error হয়?

React Suspense নিজে থেকে এরর হ্যান্ডেল করতে পারে না। এজন্য দরকার ErrorBoundary। এইজন্য আমরা একটা ErrorBoundary npm ব্যবহার করতে পারি, যা Suspense এরর হ্যান্ডেল করবে। চাইলে নিচের কোডটিও ব্যবহার করতে পারি,

// ErrorBoundary.js
import React from "react";
 
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }
 
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }
 
  render() {
    if (this.state.hasError) {
      return <h2>❌ {this.state.error.message}</h2>;
    }
    return this.props.children;
  }
}
 
export default ErrorBoundary;

এখন App.js এ র‍্যাপ করুন:

import ErrorBoundary from "./ErrorBoundary.js";
 
<ErrorBoundary>
  <Suspense fallback={<Loading />}>
    <Albums />
  </Suspense>
</ErrorBoundary>;

🧬 Nested Suspense – Step by Step Loading

React-এ আপনি চাইলে একটা বড় UI কে ধাপে ধাপে লোড করতে পারেন।

<Suspense fallback={<BigSpinner />}>
  <Biography />
  <Suspense fallback={<AlbumsGlimmer />}>
    <Albums />
  </Suspense>
</Suspense>

🌀 এটা কীভাবে কাজ করে?

  1. Biography না আসা পর্যন্ত → BigSpinner দেখাবে
  2. Biography চলে এলে → Biography দেখাবে
  3. Albums এখনো না এলে → AlbumsGlimmer দেখাবে
  4. Albums আসলে → পুরা UI ready

🧼 সারাংশ – কী শিখলাম?

শিখেছিঅর্থ
<Suspense fallback={...}>লোডিং UI দেখাতে সাহায্য করে
use()ডেটা লোড হয়ে গেলে রিটার্ন, না হলে suspend করে
Nested SuspenseUI ধাপে ধাপে দেখানো যায়
ErrorBoundaryএরর ধরতে লাগে
startTransitionUI ট্রানজিশন স্মুথ রাখে

🎁 Bonus Tip

React Suspense এখনো evolving, তাই সব ডেটা ফেচিং লাইব্রেরি (যেমন Axios) সরাসরি সাপোর্ট করে না। তবে Next.js বা Relay এর মতো ফ্রেমওয়ার্কে Suspense খুব ভালো কাজ করে।



© 2025 React JS Bangla Tutorial.