React Error Boundary 元件異常處理方案實踐

千水xxx發表於2018-07-22

在 React 專案開發中,經常有區域性元件語法錯誤讓整個應用中斷白屏的問題出現。在 React 16 版本之前,對於元件內的異常,React 是不能捕獲到的,導致區域性元件的渲染異常會中斷整個應用的執行,對使用者體驗有較大影響。終於,在 16 版本,React 新增了 Error Boundaries (錯誤邊界) 方案,最近在專案中是用來這一方案優化體驗,簡單分享下。

React Error Boundary 簡介

React 提供的方案是,通過一個異常邊界元件,將可能出現異常的元件,作為邊界元件的子元件返回,當元件出現異常無法渲染時,異常邊界元件捕獲異常,並顯示可自定義的代替內容來替換異常元件,防止應用中斷。

React 應用異常捕獲

  • 全域性捕獲
  • try-catch 捕獲部分程式碼異常
  • Error Boundary 捕獲元件異常

Error Boundary 用法

首先,舉個栗子,再來細說

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

  componentDidCatch(error, info) {
    this.setState({ hasError: true })
    logErrorToMyService(error, info)
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>
    }
    return this.props.children
  }
}
複製程式碼

如上,是 react 官方提供的示例元件,用法也很簡單,如下的父子元件:

class Parent extends Component {
  render() {
    <div>
      <Children />
    </div>
  }
}
複製程式碼

只需將子元件用 ErrorBoundary 元件包裹就可以完成錯誤邊界方案實現

class Parent extends Component {
  render() {
    <div>
      <ErrorBoundary>
        <Children />
      </ErrorBoundary>
    </div>
  }
}
複製程式碼

說明

  • 如此,子元件出現異常後,ErrorBoundary 捕獲到 Children 元件的異常
  • componentDidCatch 生命週期中,可獲取到錯誤物件 error 和 元件呼叫棧 info(info.componentStack),然後用自定義內容代替 Children 元件。
  • 可在 componentDidCatch 中,對錯誤資訊進行操作,比如提交到伺服器儲存錯誤日誌 logErrorToMyService
  • 可捕獲到的子元件異常
    • constructor 建構函式異常
    • 生命週期異常
    • render 函式異常
  • 不能捕獲到的異常
    • 事件處理異常
    • 非同步任務異常
    • 服務端異常
    • ErrorBoundary 元件自身異常

實踐優化

異常捕獲元件是可以正常使用了,但是,如果需要細粒度的對很多元件進行錯誤捕獲,就需要在引入這些子元件的父元件中全部加上 ErrorBoundary 元件包裹了,這樣很不優雅

  • errorCatch 高階元件實現

我們可以想到,其實是將 ErrorBoundary 元件作為子元件的父元件,原來的父元件引入了 ErrorBoundary 包裹後的的子元件,不難想到,可以用高階函式(高階元件)的形式優化

定義全域性工具函式:

import ErrorCatch from '../xxx/ErrorCatch'

function errorCatch(Target) {
  class Wrap extends Component {
    render() {
      return (
        <ErrorCatch>
          <Target {...this.props} />
        </ErrorCatch>
      )
    }
  }
  return Wrap
}
export default errorCatch
複製程式碼

需要注意,TargetWrap 的子元件,Wrap 是原父元件的子元件,所以必須傳入原父元件的 propsTarget 如上定義 errorCatch 函式,作為全域性工具函式,只需對需要異常捕獲的元件呼叫 errorCatch 處理即可:

import errorCatch from '../xxx/xxx/errorCatch'
...
// export default Children
export default errorCatch(Children)
複製程式碼
  • errorCatch 裝飾器實現

由高階元件,可以聯想到使用裝飾器實現,如下程式碼

@errorCatch
class Children extends Component{
  ...
}
複製程式碼

相關文章