Advance React শিখুন বাংলায় (পর্ব ৭) — Error Boundary ও Code Splitting শিখুন

Error Boundary ও Code Splitting

আগের পর্বে আমরা React.memo দিয়ে re-render নিয়ন্ত্রণ শিখেছিলাম। এই পর্বে দুটো ভিন্ন কিন্তু গুরুত্বপূর্ণ টপিক দেখব — Error Boundary (একটা component ক্র্যাশ করলে পুরো অ্যাপ যেন না ভাঙে) আর Code Splitting (অ্যাপের সাইজ ভাগ করে দ্রুত লোড করা)।

সমস্যাটা কী? — একটা এরর, পুরো অ্যাপ সাদা স্ক্রিন

Advanced JS পর্ব ১০-এ আমরা try/catch শিখেছিলাম, কিন্তু সেটা শুধু সাধারণ JavaScript ফাংশনের জন্য কাজ করে — render করার সময় কোনো component-এ এরর হলে (যেমন undefined.property অ্যাক্সেস করা), try/catch সেটা ধরতে পারে না — পুরো React অ্যাপ "unmount" হয়ে যায়, ব্যবহারকারী একটা সাদা স্ক্রিন দেখে।

function UserProfile({ user }) {
  // যদি কোনো কারণে user undefined হয়ে যায়...
  return <h1>{user.name}</h1>; // ❌ Cannot read properties of undefined
  // এই এরর পুরো অ্যাপ ক্র্যাশ করে দেবে, শুধু এই component-টা না!
}

Error Boundary — একটা "সেফটি নেট"

Error Boundary একটা বিশেষ component যেটা তার চাইল্ড component-এ render-এর সময় হওয়া এরর "ধরে" ফেলে, আর পুরো অ্যাপ ভেঙে পড়ার বদলে একটা fallback UI দেখায়।

গুরুত্বপূর্ণ প্রযুক্তিগত সীমাবদ্ধতা: Error Boundary বর্তমানে শুধু Class Component দিয়েই বানানো যায় — এখনো কোনো Hook-ভিত্তিক সমতুল্য নেই। এই সিরিজে আমরা সবসময় Functional Component ব্যবহার করেছি, কিন্তু এই একটা ক্ষেত্রে Class Component-ই একমাত্র উপায়।

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props); // Advanced JS পর্ব ৮-এর super() মনে আছে?
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // এরর হলে state আপডেট করে fallback UI দেখানোর সংকেত দেওয়া
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // এরর লগ করার জায়গা (যেমন কোনো error tracking সার্ভিসে পাঠানো)
    console.error("Error Boundary ধরেছে:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h2>কিছু একটা ভুল হয়েছে। পেজ রিফ্রেশ করে দেখুন।</h2>;
    }
    return this.props.children; // এরর না হলে স্বাভাবিকভাবে children দেখানো
  }
}

এখানে Advanced JS পর্ব ৭-এর Class ও Constructor, আর পর্ব ৮-এর Inheritance (extends React.Component) সরাসরি প্রয়োগ হচ্ছে — এই একটা জায়গায় এসে আগে শেখা OOP ধারণা কাজে লাগছে।

ব্যবহার — children হিসেবে wrap করা

React এর হাতেখড়ি পর্ব ৪-এর children prop এখানে মূল কৌশল — Error Boundary যেকোনো component-কে "wrap" করতে পারে।

function App() {
  return (
    <div>
      <Header /> {/* এখানে এরর হলে পুরো অ্যাপ ভাঙবে */}
      <ErrorBoundary>
        <UserProfile user={user} /> {/* এখানে এরর হলে শুধু এই অংশ ভাঙবে */}
      </ErrorBoundary>
      <Footer /> {/* এটা তবুও ঠিকমতো দেখাবে */}
    </div>
  );
}

এভাবে একটা নির্দিষ্ট, "ঝুঁকিপূর্ণ" অংশ (যেমন থার্ড-পার্টি ডেটা দেখানো একটা widget) আলাদা করে wrap করলে, সেটা ভেঙে গেলেও Header আর Footer ঠিকমতো কাজ করবে।

লাইভ ডেমো — এরর ধরা ও fallback দেখানো

নিচে একটা বাটন চাপলে একটা component ইচ্ছাকৃতভাবে ক্র্যাশ করবে — Error Boundary সেটা ধরে fallback UI দেখাবে, বাকি পেজ ঠিকই কাজ করবে।

Error Boundary কী ধরে না

একটা গুরুত্বপূর্ণ সীমাবদ্ধতা — Error Boundary শুধু render করার সময় হওয়া এরর ধরে। এগুলো ধরে না:

  • Event handler-এর ভেতরের এরর (এখানে সাধারণ try/catch ব্যবহার করতে হয়, Advanced JS পর্ব ১০-এর মতো)
  • Asynchronous কোড (setTimeout, fetch-এর ভেতরের এরর — পর্ব ১৩-এর মতো try/catch লাগবে)
  • Error Boundary নিজের ভেতরের এরর

এখন Code Splitting — সমস্যাটা কী?

React এর হাতেখড়ি পর্ব ১-এ npm run build চালালে একটা মাত্র (বা কয়েকটা) বড় JavaScript ফাইল তৈরি হয়, যেটাতে পুরো অ্যাপের সব কোড থাকে। প্রজেক্ট বড় হলে এই ফাইল বিশাল হয়ে যায় — ব্যবহারকারীকে পুরো ফাইল ডাউনলোড করতে হয়, এমনকি যদি সে শুধু হোমপেজ দেখতে চায়, অ্যাডমিন প্যানেল দেখার দরকার না থাকলেও।

React.lazy দিয়ে সমাধান

React.lazy একটা component-কে "লেজি" (অলস) বানায় — এটা শুধুমাত্র দরকার হলেই (প্রথম রেন্ডার হওয়ার সময়) ডাউনলোড হয়, আগে থেকে না।

// ❌ সাধারণ import — এই component-টাও প্রথম লোডেই সাথে আসবে
import AdminPanel from "./AdminPanel.jsx";

// ✅ React.lazy — এই component আলাদা ফাইলে থাকবে, দরকার হলেই লোড হবে
const AdminPanel = React.lazy(() => import("./AdminPanel.jsx"));

এখানে import("./AdminPanel.jsx") — Advanced JS পর্ব ১১-এর সাধারণ static import না, এটা একটা dynamic import, যেটা একটা Promise রিটার্ন করে (তাই Advanced JS পর্ব ৬-এর Promise ধারণা এখানে প্রাসঙ্গিক)। Vite এটা দেখে স্বয়ংক্রিয়ভাবে AdminPanel-কে একটা আলাদা ফাইলে ভাগ করে দেয়।

Suspense — লোড হওয়ার সময় কী দেখাবে

যেহেতু lazy component ডাউনলোড হতে কিছুটা সময় লাগে, <Suspense> দিয়ে সেই সময়ে একটা fallback UI দেখানো হয় — অনেকটা পর্ব ১৩-এর isLoading state-এর মতোই, কিন্তু এবার পুরো component-ই লোড হচ্ছে, শুধু ডেটা না।

import { Suspense } from "react";

const AdminPanel = React.lazy(() => import("./AdminPanel.jsx"));

function App() {
  const [showAdmin, setShowAdmin] = React.useState(false);

  return (
    <div>
      <button onClick={() => setShowAdmin(true)}>অ্যাডমিন প্যানেল দেখাও</button>

      {showAdmin && (
        <Suspense fallback={<p>লোড হচ্ছে...</p>}>
          <AdminPanel />
        </Suspense>
      )}
    </div>
  );
}

এখন AdminPanel-এর কোড শুধু তখনই ডাউনলোড হবে যখন ব্যবহারকারী প্রথমবার বাটনে চাপবে — আর সেই সময়টুকু Suspense "লোড হচ্ছে..." দেখাবে।

লাইভ ডেমো — Suspense দিয়ে লেজি লোডিং সিমুলেট করা

Blogger পোস্টের ভেতরে থাকায় সত্যিকারের ফাইল-স্প্লিটিং দেখানো সম্ভব না, কিন্তু Suspense-এর আচরণ ঠিক এভাবেই কাজ করে — একটা কৃত্রিম দেরি দিয়ে সিমুলেট করা হলো।

বাটনে চাপলে দেড় সেকেন্ড "লোড হচ্ছে" দেখাবে, তারপর প্যানেল আসবে — বাস্তব প্রজেক্টে এই সময়টা হয় নেটওয়ার্ক থেকে আসল JS ফাইল ডাউনলোড হওয়ার সময়।

কোথায় Code Splitting সবচেয়ে বেশি কাজে লাগে

জায়গা কেন
রাউট-ভিত্তিক (React Router পর্ব ১৫) /admin পেজের কোড হোমপেজ দেখার সময় লাগে না
বড়, কম-ব্যবহৃত widget যেমন একটা চার্ট লাইব্রেরি, শুধু ড্যাশবোর্ড পেজেই দরকার
মডাল/পপ-আপ ব্যবহারকারী নাও খুলতে পারে, তাই আগে থেকে লোড করার দরকার নেই

Router-এর সাথে মিলিয়ে ব্যবহার করলে সবচেয়ে বেশি লাভ হয়:

import { Routes, Route } from "react-router-dom";

const Home = React.lazy(() => import("./pages/Home.jsx"));
const Dashboard = React.lazy(() => import("./pages/Dashboard.jsx"));
const Admin = React.lazy(() => import("./pages/Admin.jsx"));

function App() {
  return (
    <Suspense fallback={<p>লোড হচ্ছে...</p>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/admin" element={<Admin />} />
      </Routes>
    </Suspense>
  );
}

এখন প্রতিটা পেজের কোড আলাদা "চাংক" হয়ে যাবে — ব্যবহারকারী হোমপেজে থাকলে Admin.jsx ডাউনলোডই হবে না।

একটা সম্পূর্ণ উদাহরণ — Error Boundary + Lazy একসাথে

দুটো টেকনিক একসাথেও ব্যবহার হয় — একটা lazy component লোড করতে ব্যর্থ হলে (যেমন নেটওয়ার্ক সমস্যা), Error Boundary সেই ব্যর্থতা ধরতে পারে।

const HeavyChart = React.lazy(() => import("./HeavyChart.jsx"));

function Dashboard() {
  return (
    <ErrorBoundary>
      <Suspense fallback={<p>চার্ট লোড হচ্ছে...</p>}>
        <HeavyChart />
      </Suspense>
    </ErrorBoundary>
  );
}

এভাবে চার্ট লোড হওয়ার সময় (Suspense) আর লোড ব্যর্থ হলে (ErrorBoundary) — দুটো পরিস্থিতিই সামলানো যায়, ড্যাশবোর্ডের বাকি অংশ প্রভাবিত না করে।

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

Exercise:

  1. উপরের ErrorBoundary ক্লাস নিজে বানাও, দুইটা component wrap করো — একটা সবসময় ঠিক থাকবে, একটা মাঝে মাঝে ক্র্যাশ করবে।
  2. নিজের কোনো Vite প্রজেক্টে সত্যিকারের React.lazy ব্যবহার করো, তারপর npm run build চালিয়ে dist/ ফোল্ডারে একাধিক JS ফাইল তৈরি হয়েছে কিনা দেখো।
  3. React Router-এর সাথে (পর্ব ১৫) মিলিয়ে route-ভিত্তিক code splitting সেটআপ করো।
  4. ভেবে দেখো — তোমার React এর হাতেখড়ি মিনি প্রজেক্টের (পর্ব ১৮) কোন অংশ Error Boundary দিয়ে সুরক্ষিত রাখা উচিত ছিল?

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

এটাই Advanced React সিরিজের শেষ থিওরি পর্ব। পরের পর্বে (সিরিজের শেষ পর্ব) আমরা useReducer, Context, useMemo/useCallback, Custom Hook, React.memo, আর Error Boundary — সবকিছু মিলিয়ে একটা বড়, বাস্তবঘনিষ্ঠ Mini Project বানাব।

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

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

যোগাযোগ ফর্ম