文章

React 錯誤邊界 (Error Boundaries)

React 錯誤邊界 (Error Boundaries)

在 React 應用中,如果某個組件發生錯誤,它可能會導致整個應用崩潰。為了提高應用的穩定性,React 引入了 錯誤邊界(Error Boundaries) 機制,用來捕獲並處理 UI 渲染中的錯誤,防止錯誤擴散影響整個應用。


1. 錯誤邊界的基本概念

  • 錯誤邊界 是一個 React 組件,當其子組件中的渲染、生命周期方法或構造函數中發生錯誤時,會捕獲到錯誤並顯示一個備用的 UI,而不會崩潰整個應用。
  • 錯誤邊界只能捕獲子組件樹中的錯誤,無法捕獲其自身的錯誤,也無法捕獲事件處理中的錯誤(需要手動捕獲)。

2. 創建錯誤邊界

錯誤邊界必須是類組件,並且需要實現以下兩個方法:

  1. static getDerivedStateFromError(error):當子組件拋出錯誤時調用,讓你更新錯誤邊界的狀態。
  2. componentDidCatch(error, info):當子組件拋出錯誤時調用,讓你記錄錯誤訊息(如錯誤日誌)。

基本範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React from 'react';

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

  // 當捕獲錯誤時更新狀態,顯示備用 UI
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  // 錯誤處理:可以記錄錯誤訊息
  componentDidCatch(error, errorInfo) {
    console.log('Error caught:', error, errorInfo);
    // 可以在此處上報錯誤到服務器
  }

  render() {
    if (this.state.hasError) {
      // 顯示替代的 UI
      return <h1>出現錯誤,請稍後再試。</h1>;
    }

    // 正常渲染子組件
    return this.props.children;
  }
}

export default ErrorBoundary;

使用方式:

1
2
3
4
5
6
7
function App() {
  return (
    <ErrorBoundary>
      <MyComponent />
    </ErrorBoundary>
  );
}

在這個範例中,當 MyComponent 或其子組件發生錯誤時,ErrorBoundary 組件會捕獲錯誤並顯示備用 UI,而不會讓整個應用崩潰。


3. 錯誤邊界能捕獲哪些錯誤?

錯誤邊界能捕獲以下情況下的錯誤:

  1. 渲染階段:如果某個組件在渲染階段發生錯誤。
  2. 生命周期方法:如果在某個組件的 componentDidMountcomponentDidUpdate 等生命周期方法中發生錯誤。
  3. 構造函數:如果某個組件的構造函數中發生錯誤。

4. 錯誤邊界無法捕獲的錯誤

  1. 事件處理器:錯誤邊界無法捕獲組件的事件處理器中的錯誤。例如,按鈕點擊事件的錯誤不會被錯誤邊界捕獲。對於這類錯誤,需要使用 JavaScript 的 try...catch 自行捕獲。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
     function MyButton() {
       const handleClick = () => {
         try {
           // 可能會拋出錯誤的代碼
         } catch (error) {
           console.error("Error caught:", error);
         }
       };
    
       return <button onClick={handleClick}>點我</button>;
     }
    
  2. 非同步代碼:例如 setTimeoutPromise 中的錯誤,也無法通過錯誤邊界捕獲,需要在異步代碼中使用 try...catch.catch() 來處理錯誤。

    1
    2
    3
    4
    5
    6
    7
    
     async function fetchData() {
       try {
         const response = await fetch('/api/data');
       } catch (error) {
         console.error("Error caught:", error);
       }
     }
    
  3. 服務端渲染:錯誤邊界只能在瀏覽器中捕獲錯誤,無法捕獲服務端渲染過程中的錯誤。
  4. 自身錯誤:如果錯誤邊界自身出現錯誤,它無法捕獲自己的錯誤。需要有外層的錯誤邊界來捕獲。

5. 錯誤邊界的應用場景

  • 應用的穩定性:在生產環境中,錯誤邊界可以防止整個應用因為某個局部錯誤而崩潰,並顯示友好的備用 UI。
  • 第三方組件的使用:當使用第三方組件或插件時,錯誤邊界可以保護應用免受外部組件導致的崩潰。
  • 錯誤監控與日誌componentDidCatch 可以記錄錯誤日誌或上報錯誤給監控系統,以便開發者分析和修復問題。

6. 實踐建議

  1. 為關鍵區域設置錯誤邊界:在應用的某些重要區域(如導航欄、主要內容區等)設置錯誤邊界,保證即使發生錯誤,應用的其他部分仍然可以正常工作。

  2. 根組件的錯誤邊界:在應用的根組件上設置一個全局錯誤邊界,來捕獲任何未預料到的錯誤。

  3. 針對用戶體驗的備用 UI:為用戶提供友好的錯誤提示,讓他們知道問題出現在哪裡,並提供解決方法(如重新加載頁面或聯繫支持)。


7. 總結

  • 錯誤邊界是 React 用來提高應用穩定性的一個重要機制,能夠捕獲渲染過程中的錯誤,避免應用崩潰。
  • 錯誤邊界必須是類組件,並實現 getDerivedStateFromErrorcomponentDidCatch 這兩個方法。
  • 它能捕獲渲染階段和生命周期中的錯誤,但無法捕獲事件處理器和非同步代碼中的錯誤。

通過使用錯誤邊界,我們可以構建更加可靠、健壯的 React 應用,提供更好的用戶體驗和錯誤處理機制。

本文章以 CC BY 4.0 授權