在 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
複製程式碼
需要注意,Target
是 Wrap
的子元件,Wrap
是原父元件的子元件,所以必須傳入原父元件的 props
到 Target
如上定義 errorCatch
函式,作為全域性工具函式,只需對需要異常捕獲的元件呼叫 errorCatch
處理即可:
import errorCatch from '../xxx/xxx/errorCatch'
...
// export default Children
export default errorCatch(Children)
複製程式碼
- errorCatch 裝飾器實現
由高階元件,可以聯想到使用裝飾器實現,如下程式碼
@errorCatch
class Children extends Component{
...
}
複製程式碼