Mục lục

React Error Boundaries: Chặn đứng 'Màn hình trắng'

Hiểu đúng về Error Boundary: Nó bắt được gì và không bắt được gì? Tại sao try-catch vô dụng trong JSX? Hướng dẫn sử dụng thư viện react-error-boundary chuẩn hiện đại.

Trong JavaScript thuần, nếu một dòng code lỗi -> Script dừng lại. Trong React, nếu một Component lỗi -> Toàn bộ cây DOM bị gỡ bỏ (Trang trắng tinh "White Screen of Death"). Error Boundary là cách để cô lập vùng lỗi đó.

1. Hiện trạng Function Component (2025)

Câu hỏi: "Tôi ghét Class Component. Tôi có thể viết Error Boundary bằng Function/Hook không?" Trả lời: Về mặt kỹ thuật là KHÔNG. Tính đến React 19, các lifecycle getDerivedStateFromErrorcomponentDidCatch chỉ tồn tại trong Class Component. Chưa có Hook tương đương.

NHƯNG:

  1. Next.js App Router: File error.tsx LÀ Function Component. (Next.js đã wrap Class ngầm bên dưới cho bạn).
  2. External Library: Thư viện react-error-boundary giúp bạn dùng Error Boundary theo phong cách Functional Programming 100%.

2. Cách dùng react-error-boundary (Khuyên dùng)

Đây là chuẩn mực hiện đại. Bạn không bao giờ phải viết class nữa.

tsx:
import { ErrorBoundary } from "react-error-boundary";

// 1. Fallback Component (Là Function Component bình thường)
// Nhận props { error, resetErrorBoundary }
function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert" className="p-4 bg-red-100 text-red-900 rounded">
      <p className="font-bold">Oops! Có lỗi xảy ra:</p>
      <pre className="text-sm">{error.message}</pre>
      
      {/* Nút Reset giúp user thử lại mà không cần F5 trang */}
      <button 
        onClick={resetErrorBoundary}
        className="mt-2 px-4 py-2 bg-red-600 text-white rounded"
      >
        Thử lại
      </button>
    </div>
  );
}

// 2. Wrap Component
function App() {
  return (
    // onReset: Nơi bạn reset state của app về ban đầu để component con render lại sạch sẽ
    <ErrorBoundary 
      FallbackComponent={ErrorFallback}
      onReset={() => {
        // Ví dụ: Reset form, clear cache
        console.log("Resetting app state...");
      }}
    >
      <ProductList />
    </ErrorBoundary>
  );
}

3. Bắt lỗi trong Event Handler (useErrorBoundary)

Lỗi trong onClick (Event Handler) KHÔNG làm crash React Render, nên Error Boundary mặc định không bắt được. Muốn bắt lỗi này (để hiện UI lỗi đồng bộ), bạn phải dùng React 19 useActionState hoặc thư viện useErrorBoundary.

tsx:
import { useErrorBoundary } from "react-error-boundary";

function LoginButton() {
  const { showBoundary } = useErrorBoundary();

  const handleClick = async () => {
    try {
      await loginAPI();
    } catch (error) {
      // 🚀 Trick: Ném lỗi vào vòng Render để Error Boundary trên cao bắt được
      showBoundary(error);
    }
  };

  return <button onClick={handleClick}>Login</button>;
}

4. Class Component (Dành cho ai tò mò)

Nếu bạn không muốn cài thư viện, bạn phải tự viết Class này (Copy-paste cái này dùng cho cả dự án).

tsx:
class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // Gửi log lỗi lên Server/Sentry
    logErrorToService(error, info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback;
    }
    return this.props.children;
  }
}

Kết luận

  • Nếu dùng Next.js App Router -> Dùng error.tsx (Function Component).
  • Nếu dùng React thường (Vite) -> Dùng react-error-boundary.
  • Quên Class Component đi (trừ khi bạn đang maintain code cũ 5 năm trước).
Quảng cáo
mdhorizontal