Tiếng Việt

Làm chủ React Error Boundaries để xây dựng ứng dụng linh hoạt và thân thiện với người dùng. Tìm hiểu các phương pháp hay nhất, kỹ thuật triển khai và chiến lược xử lý lỗi nâng cao.

React Error Boundaries: Kỹ thuật Xử lý Lỗi Mềm Mại cho Ứng dụng Bền vững

Trong thế giới phát triển web năng động, việc tạo ra các ứng dụng mạnh mẽ và thân thiện với người dùng là điều tối quan trọng. React, một thư viện JavaScript phổ biến để xây dựng giao diện người dùng, cung cấp một cơ chế mạnh mẽ để xử lý lỗi một cách nhẹ nhàng: Error Boundaries. Hướng dẫn toàn diện này đi sâu vào khái niệm về Error Boundaries, khám phá mục đích, cách triển khai và các phương pháp hay nhất để xây dựng các ứng dụng React bền vững.

Hiểu về Sự cần thiết của Error Boundaries

Các component của React, giống như bất kỳ đoạn mã nào, đều có thể gặp lỗi. Những lỗi này có thể xuất phát từ nhiều nguồn khác nhau, bao gồm:

Nếu không có cơ chế xử lý lỗi phù hợp, một lỗi trong một component React có thể làm sập toàn bộ ứng dụng, dẫn đến trải nghiệm người dùng kém. Error Boundaries cung cấp một cách để bắt những lỗi này và ngăn chúng lan truyền lên cây component, đảm bảo rằng ứng dụng vẫn hoạt động ngay cả khi các component riêng lẻ bị lỗi.

React Error Boundaries là gì?

Error Boundaries là các component React bắt lỗi JavaScript ở bất kỳ đâu trong cây component con của chúng, ghi lại các lỗi đó và hiển thị một giao diện người dùng dự phòng thay vì cây component đã bị sập. Chúng hoạt động như một mạng lưới an toàn, ngăn chặn lỗi làm sập toàn bộ ứng dụng.

Các đặc điểm chính của Error Boundaries:

Triển khai Error Boundaries

Hãy cùng xem qua quy trình tạo một component Error Boundary cơ bản:

1. Tạo Component Error Boundary

Đầu tiên, tạo một class component mới, ví dụ, có tên là ErrorBoundary:


import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false
    };
  }

  static getDerivedStateFromError(error) {
    // Cập nhật state để lần render tiếp theo sẽ hiển thị UI dự phòng.
    return {
      hasError: true
    };
  }

  componentDidCatch(error, errorInfo) {
    // Bạn cũng có thể ghi log lỗi tới một dịch vụ báo cáo lỗi
    console.error("Caught error: ", error, errorInfo);
    // Ví dụ: logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // Bạn có thể render bất kỳ UI dự phòng tùy chỉnh nào
      return (
        <div>
          <h2>Đã có lỗi xảy ra.</h2>
          <details style={{ whiteSpace: 'pre-wrap' }}>
            {this.state.error && this.state.error.toString()}
            <br />
            {this.state.errorInfo.componentStack}
          </details>
        </div>
      );
    }

    return this.props.children; 
  }
}

export default ErrorBoundary;

Giải thích:

2. Sử dụng Error Boundary

Để sử dụng Error Boundary, chỉ cần bọc bất kỳ component nào có thể ném ra lỗi bằng component ErrorBoundary:


import ErrorBoundary from './ErrorBoundary';

function MyComponent() {
  // Component này có thể ném ra lỗi
  return (
    <ErrorBoundary>
      <PotentiallyBreakingComponent />
    </ErrorBoundary>
  );
}

export default MyComponent;

Nếu PotentiallyBreakingComponent ném ra lỗi, ErrorBoundary sẽ bắt nó, ghi log lỗi và render UI dự phòng.

3. Ví dụ Minh họa trong Bối cảnh Toàn cục

Hãy xem xét một ứng dụng thương mại điện tử hiển thị thông tin sản phẩm được lấy từ một máy chủ từ xa. Một component, ProductDisplay, chịu trách nhiệm render chi tiết sản phẩm. Tuy nhiên, máy chủ đôi khi có thể trả về dữ liệu không mong muốn, dẫn đến lỗi render.


// ProductDisplay.js
import React from 'react';

function ProductDisplay({ product }) {
  // Mô phỏng một lỗi tiềm ẩn nếu product.price không phải là một số
  if (typeof product.price !== 'number') {
    throw new Error('Giá sản phẩm không hợp lệ');
  }

  return (
    <div>
      <h2>{product.name}</h2>
      <p>Giá: {product.price}</p>
      <img src={product.imageUrl} alt={product.name} />
    </div>
  );
}

export default ProductDisplay;

Để bảo vệ khỏi các lỗi như vậy, hãy bọc component ProductDisplay bằng một ErrorBoundary:


// App.js
import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import ProductDisplay from './ProductDisplay';

function App() {
  const product = {
    name: 'Sản phẩm ví dụ',
    price: 'Không phải là số', // Dữ liệu cố tình sai
    imageUrl: 'https://example.com/image.jpg'
  };

  return (
    <div>
      <ErrorBoundary>
        <ProductDisplay product={product} />
      </ErrorBoundary>
    </div>
  );
}

export default App;

Trong kịch bản này, vì product.price được cố tình đặt thành một chuỗi thay vì một số, component ProductDisplay sẽ ném ra một lỗi. ErrorBoundary sẽ bắt lỗi này, ngăn toàn bộ ứng dụng bị sập và hiển thị UI dự phòng thay vì component ProductDisplay bị hỏng.

4. Error Boundaries trong Ứng dụng Quốc tế hóa

Khi xây dựng các ứng dụng cho khán giả toàn cầu, các thông báo lỗi nên được bản địa hóa để cung cấp trải nghiệm người dùng tốt hơn. Error Boundaries có thể được sử dụng kết hợp với các thư viện quốc tế hóa (i18n) để hiển thị các thông báo lỗi đã được dịch.


// ErrorBoundary.js (với hỗ trợ i18n)
import React from 'react';
import { useTranslation } from 'react-i18next'; // Giả sử bạn đang sử dụng react-i18next

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false,
      error: null,
      errorInfo: null,
    };
  }

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

  componentDidCatch(error, errorInfo) {
    console.error("Caught error: ", error, errorInfo);
    this.setState({errorInfo: errorInfo});
  }

  render() {
    if (this.state.hasError) {
      return (
        <FallbackUI error={this.state.error} errorInfo={this.state.errorInfo}/>
      );
    }

    return this.props.children;
  }
}

const FallbackUI = ({error, errorInfo}) => {
  const { t } = useTranslation();

  return (
    <div>
      <h2>{t('error.title')}</h2>
      <p>{t('error.message')}</p>
      <details style={{ whiteSpace: 'pre-wrap' }}>
        {error && error.toString()}<br />
        {errorInfo?.componentStack}
      </details>
    </div>
  );
}


export default ErrorBoundary;

Trong ví dụ này, chúng tôi sử dụng react-i18next để dịch tiêu đề và thông báo lỗi trong UI dự phòng. Các hàm t('error.title')t('error.message') sẽ lấy các bản dịch phù hợp dựa trên ngôn ngữ đã chọn của người dùng.

5. Lưu ý đối với Kết xuất phía Máy chủ (SSR)

Khi sử dụng Error Boundaries trong các ứng dụng được kết xuất phía máy chủ, việc xử lý lỗi một cách thích hợp là rất quan trọng để ngăn máy chủ bị sập. Tài liệu của React khuyên bạn nên tránh sử dụng Error Boundaries để phục hồi sau các lỗi render trên máy chủ. Thay vào đó, hãy xử lý lỗi trước khi render component hoặc render một trang lỗi tĩnh trên máy chủ.

Các Thực hành Tốt nhất khi Sử dụng Error Boundaries

Các Chiến lược Xử lý Lỗi Nâng cao

1. Cơ chế Thử lại

Trong một số trường hợp, có thể phục hồi sau một lỗi bằng cách thử lại thao tác đã gây ra nó. Ví dụ, nếu một yêu cầu mạng không thành công, bạn có thể thử lại sau một khoảng thời gian ngắn. Error Boundaries có thể được kết hợp với các cơ chế thử lại để cung cấp trải nghiệm người dùng linh hoạt hơn.


// ErrorBoundaryWithRetry.js
import React from 'react';

class ErrorBoundaryWithRetry extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false,
      retryCount: 0,
    };
  }

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

  componentDidCatch(error, errorInfo) {
    console.error("Caught error: ", error, errorInfo);
  }

  handleRetry = () => {
    this.setState(prevState => ({
      hasError: false,
      retryCount: prevState.retryCount + 1,
    }), () => {
      // Điều này buộc component phải render lại. Hãy xem xét các mẫu tốt hơn với các props được kiểm soát.
      this.forceUpdate(); // CẢNH BÁO: Sử dụng cẩn thận
      if (this.props.onRetry) {
          this.props.onRetry();
      }
    });
  };

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h2>Đã có lỗi xảy ra.</h2>
          <button onClick={this.handleRetry}>Thử lại</button>
        </div>
      );
    }

    return this.props.children;
  }
}

export default ErrorBoundaryWithRetry;

Component ErrorBoundaryWithRetry bao gồm một nút thử lại, khi được nhấp, sẽ đặt lại state hasError và render lại các component con. Bạn cũng có thể thêm một retryCount để giới hạn số lần thử lại. Cách tiếp cận này có thể đặc biệt hữu ích để xử lý các lỗi tạm thời, chẳng hạn như sự cố mạng tạm thời. Hãy chắc chắn rằng prop `onRetry` được xử lý tương ứng và thực hiện lại việc lấy dữ liệu/thực thi lại logic có thể đã bị lỗi.

2. Cờ Tính năng (Feature Flags)

Cờ tính năng cho phép bạn bật hoặc tắt các tính năng trong ứng dụng của mình một cách linh hoạt mà không cần triển khai mã mới. Error Boundaries có thể được sử dụng kết hợp với cờ tính năng để suy giảm chức năng một cách nhẹ nhàng trong trường hợp xảy ra lỗi. Ví dụ, nếu một tính năng cụ thể đang gây ra lỗi, bạn có thể tắt nó bằng cờ tính năng và hiển thị một thông báo cho người dùng cho biết rằng tính năng đó tạm thời không khả dụng.

3. Mẫu Circuit Breaker

Mẫu circuit breaker là một mẫu thiết kế phần mềm được sử dụng để ngăn một ứng dụng liên tục cố gắng thực hiện một hoạt động có khả năng thất bại. Nó hoạt động bằng cách theo dõi tỷ lệ thành công và thất bại của một hoạt động và, nếu tỷ lệ thất bại vượt quá một ngưỡng nhất định, nó sẽ "mở mạch" và ngăn các nỗ lực tiếp theo để thực hiện hoạt động đó trong một khoảng thời gian nhất định. Điều này có thể giúp ngăn ngừa các lỗi dây chuyền và cải thiện sự ổn định chung của ứng dụng.

Error Boundaries có thể được sử dụng để triển khai mẫu circuit breaker trong các ứng dụng React. Khi một Error Boundary bắt một lỗi, nó có thể tăng một bộ đếm lỗi. Nếu bộ đếm lỗi vượt quá một ngưỡng, Error Boundary có thể hiển thị một thông báo cho người dùng cho biết rằng tính năng đó tạm thời không khả dụng và ngăn các nỗ lực tiếp theo để thực hiện hoạt động. Sau một khoảng thời gian nhất định, Error Boundary có thể "đóng mạch" và cho phép các nỗ lực thực hiện lại hoạt động.

Kết luận

React Error Boundaries là một công cụ thiết yếu để xây dựng các ứng dụng mạnh mẽ và thân thiện với người dùng. Bằng cách triển khai Error Boundaries, bạn có thể ngăn lỗi làm sập toàn bộ ứng dụng của mình, cung cấp một UI dự phòng nhẹ nhàng cho người dùng và ghi log lỗi đến các dịch vụ giám sát để gỡ lỗi và phân tích. Bằng cách tuân theo các phương pháp hay nhất và các chiến lược nâng cao được nêu trong hướng dẫn này, bạn có thể xây dựng các ứng dụng React bền vững, đáng tin cậy và mang lại trải nghiệm người dùng tích cực, ngay cả khi đối mặt với các lỗi không mong muốn. Hãy nhớ tập trung vào việc cung cấp thông báo lỗi hữu ích được bản địa hóa cho khán giả toàn cầu.