什麼是錯誤邊界?
官方描述:過去,元件内的 JavaScript 錯誤會導緻 React 的内部狀态被破壞,并且在下一次渲染時 産生 可能無法追蹤的 錯誤。這些錯誤基本上是由較早的其他代碼(非 React 元件代碼)錯誤引起的,但 React 并沒有提供一種在元件中優雅處理這些錯誤的方式,也無法從錯誤中恢複。部分 UI 的 JavaScript 錯誤不應該導緻整個應用崩潰,為了解決這個問題,React 16 引入了一個新的概念 —— 錯誤邊界。
解讀
錯誤邊界指的是部分UI導緻的JS錯誤導緻整個應用崩潰,React為防止這種問題的發生引入 了錯誤邊界,錯誤邊界是一種元件,這種元件可以檢測發生在其子元件樹任何位置的JS錯誤,并列印這些錯誤,同時展示降級UI,而并不會展示那些發生崩潰的子元件,錯誤邊界在渲染期間,生命周期方法以及整個元件樹的構造函數中捕獲錯誤。
錯誤邊界無法捕獲哪些錯誤?
- 事件處理
- 異步代碼
- 服務端渲染
- 它自身抛出來的錯誤
渲染備用UI
- 使用static getDerivedStateFromError()
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能夠顯示降級後的 UI
return { hasError: true };
}
複制代碼
列印錯誤資訊
- 使用componentDidCatch()列印錯誤資訊
componentDidCatch(error, errorInfo) {
// 你同樣可以将錯誤日志上報給伺服器
logErrorToMyService(error, errorInfo);
}
複制代碼
列印錯誤資訊并渲染備用UI
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能夠顯示降級後的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你同樣可以将錯誤日志上報給伺服器
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 你可以自定義降級後的 UI 并渲染
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
複制代碼
- 我們可以将錯誤邊界封裝為一個元件進行使用
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
複制代碼
錯誤邊界的工作方式類似于JS中的catch,不同的地方在于錯誤邊界隻針對React元件,隻有類元件才可以成為錯誤邊界元件。
注意
- 錯誤邊界僅可以捕獲其子元件的錯誤,它無法捕獲自身的錯誤。
- 如果一個錯誤邊界無法渲染錯誤,則錯誤會冒泡至最近的上層錯誤邊界。
錯誤邊界應該放置在哪?
錯誤邊界的位置可以選在最頂層的路由元件并未使用者展示一個錯誤資訊。
未捕獲錯誤的新行為
自React16以來,任何未被錯誤邊界捕獲的錯誤将會導緻整個React元件樹被解除安裝。之是以這樣做是因為官方認為展示錯誤資訊比不顯示任何内容要糟糕。
元件棧追蹤
在開發環境下,React16會把渲染期間發生的所有錯誤列印到控制台,處了錯誤資訊和JS棧外,React16還提供了元件棧追蹤,我們可以準确的檢視發生在元件樹内的錯誤資訊

關于try/catch?
- try/catch雖好,但是隻能應用于指令式代碼
try {
showButton();
} catch (error) {
// ...
}
複制代碼
- 錯誤邊界保留了React的聲明性質。及時一個錯誤發生在componentDidUpdate方法中,并且由某一個深層元件樹的setState引起,其仍然能夠冒泡到最近的錯誤邊界。
關于事件處理器
React不需要錯誤邊界來捕獲事件處理器中的錯誤,與render方法和生命周期方法不同,事件處理器不會在渲染期間觸發,是以如果他們抛出異常,React仍然能夠知道需要在螢幕上顯示什麼。如果我們需要在事件處理器内部捕獲錯誤,我們可以使用普通的JS try/catch語句:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
try {
// 執行操作,如有錯誤則會抛出
} catch (error) {
this.setState({ error });
}
}
render() {
if (this.state.error) {
return <h1>Caught an error.</h1>
}
return <button onClick={this.handleClick}>Click Me</button>
}
}
複制代碼
React15中的命名不再使用
請将unstable_handleError替換為componentDidCatch。