React 錯誤邊界組件的處理
這是React16的內容,並不是最新的技術,但是用很少被討論,直到通過文檔發現其實也是很有用的一部分內容,還是總結一下~
React中的未捕獲的 JS 錯誤會導致整個應用的崩潰,和整個組件樹的卸載。從 React16 開始就是這樣。但是同時React也引入瞭一個新的概念——錯誤邊界。
定義,是什麼
錯誤邊界仍然是一種組件,可以捕獲(打印或者其他方式)處理該組件的子組件樹任何位置的 JavaScript 錯誤,並根據需要渲染出備用UI.
工作方式類似於try-catch,但是錯誤邊界隻用於 React 組件。
隻有class組件能夠成為錯誤邊界組件。錯誤邊界僅可以捕獲子組件的錯誤,無法捕獲自身的錯誤。
錯誤邊界會在渲染期間,生命周期和整個組件樹的構造函數中捕獲錯誤。如果沒有錯誤邊界處理,渲染的還是崩潰的子組件樹,這顯然不是我們想要的。
通過一個例子來逐步演示要怎麼用錯誤邊界:
export default class ErrorTest extends Component { constructor(props) { super(props); } render() { return ( <div> <BugCounter></BugCounter> <span>my name is dan</span> </div> ); } } // Bug 報錯組件 class BugCounter extends Component { constructor(props) { super(props); this.state = { counter: 0, }; } click = () => { this.setState(({ counter }) => ({ counter: counter + 1 })); }; render() { if (this.state.counter === 5) { throw new Error("crashed!"); } return ( <div> <h3 onClick={this.click}>{this.state.counter}</h3> </div> ); } }
上面代碼的渲染結果(忽略樣式):
點擊數字0
,會逐步遞增。但是數字等於5
的時候,組件會拋出一個Error
:
該Error
會引起整個Demo
的崩潰,連外部的<span>my name is dan</span>
也顯示不出來瞭,這時還沒有添加錯誤邊界。
生產模式下,會直接白屏,並在控制臺報錯:
getDerivedStateFromError & componentDidCatch
需要一個錯誤邊界來處理這種崩潰。如何定義一個錯誤邊界?
定義一個組件,並實現static getDerivedStateFromError()
或者componentDidCatch()
生命周期方法(可以都實現或者選擇其一)。這個組件就會變成一個錯誤邊界。
關於這兩個生命周期函數,可以通過鏈接查看,總結如下:
componentDidCatch(error, info)
error
是拋出的錯誤對象,而info
則包含瞭組件引發錯誤的棧信息。函數在提交階段被調用。是可以執行副作用的。
static getDerivedStateFromError(error)
在子組件拋出錯誤後調用,會將拋出的錯誤作為參數。需要返回一個值,以更新state。該函數在渲染階段調用,不允許出現副作用。如果在捕獲錯誤後需要執行副作用操作,應該在componentDidCatch
中進行。
制作錯誤邊界組件
可以使用組合的方式,在要使用的組件上面添加一個錯誤邊界組件包裹一層。該組件需要這些效果:
- 捕獲子組件錯誤,組件內部記錄出錯狀態
- 在出錯狀態下顯示備用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; } }
捕獲到錯誤之後的副作用是自定義的,上傳服務器,或者用state
記錄再顯示在頁面上:
componentDidCatch(error, errorInfo) { // Catch errors in any components below and re-render with error message this.setState({ error: error, errorInfo: errorInfo }) }
捕獲處理
加上所有代碼,將有問題的組件用錯誤邊界的組件包裹起來,看看結果:
import { Component } from "react"; export default class ErrorTest extends Component { render() { return ( <div> <ErrorBoundary> <BugCounter></BugCounter> </ErrorBoundary> <span>my name is dan</span> </div> ); } } // Bug 報錯組件 class BugCounter extends Component { constructor(props) { super(props); this.state = { counter: 0, }; } click = () => { this.setState(({ counter }) => ({ counter: counter + 1 })); }; render() { if (this.state.counter === 5) { throw new Error("crashed!"); } return ( <div> <h3 onClick={this.click}>{this.state.counter}</h3> </div> ); } } // 錯誤邊界處理組件 class ErrorBoundary extends Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // 更新 state 使下一次渲染能夠顯示降級後的 UI return { hasError: true }; } render() { if (this.state.hasError) { // 你可以自定義降級後的 UI 並渲染 return <h1>Something went wrong.</h1>; } return this.props.children; } }
拋出異常在開發模式下依然是報錯的,但是在使用yarn build
之後,再通過http-server
掛起來之後,訪問生產的頁面:
可以看到,雖然因為throw error
控制臺出錯,但是my name is dan
的顯示並沒有被影響,也就是說,錯誤邊界內部的子組件錯誤沒有影響到外部其他組件和元素。
作用范圍
錯誤邊界用於處理子組件生命周期和渲染函數上的錯誤,對於事件處理器,不會在渲染期間觸發,對於事件處理器拋出的異常應該用try catch
。
錯誤邊界無法捕獲這些場景中的錯誤:
- 事件處理
- 異步代碼
- 服務端渲染
- 錯誤邊界自身拋出的錯誤(非子組件)
關於錯誤邊界,一個 React
的官方demo
值得嘗試:
https://codepen.io/gaearon/pen/wqvxGa?editors=0010
參考:
https://zh-hans.reactjs.org/docs/error-boundaries.html
https://zh-hans.reactjs.org/docs/react-component.html
https://codepen.io/gaearon/pen/wqvxGa?editors=0010
到此這篇關於React 錯誤邊界組件的處理的文章就介紹到這瞭,更多相關React 錯誤邊界內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- None Found