前言:做筆記,參考:react文檔,文章涉及的示例:https://sanhuamao1.coding.net/public/note/myapp/git/files/13Error-Boundaries
錯誤邊界
錯誤邊界是一種 React 組件,可以捕獲並打印發生在其子組件樹任何位置的 JavaScript 錯誤,並且渲染出備用 UI。錯誤邊界在渲染期間、生命周期和整個組件樹的構造函數中捕獲錯誤。
它無法在以下場景捕獲錯誤:
- 事件處理(了解更多)
- 異步代碼(例如 setTimeout 或 requestAnimationFrame 回調函數)
- 服務端渲染
- 它自身拋出來的錯誤(並非它的子組件)
使用方式
這是兩種生命周期方法。只要class組件用了任意一種,那它就變成一個錯誤邊界組件
componentDidCatch()
:打印錯誤static getDerivedStateFromError()
:渲染備用 UI
方式一:componentDidCatch()
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, errorInfo) {
this.setState({
error:error,
errorInfo:errorInfo
})
}
render() {
if (this.state.errorInfo) {
return <div>
{this.state.error&&this.state.error.toString()}<br/> {this.state.errorInfo.componentStack}
</div>
}
//正常則返回子元素,即該組件包裹的元素
return this.props.children;
}
}
一個測試組件,如果輸入的是非字母就會報錯:
class Example extends React.Component{
constructor(props){
super(props)
this.state={value:""}
this.handleChange=(e)=>{
this.setState({value:e.target.value})
}
}
render(){
const value=this.state.value
if(/^[a-zA-Z]+$/.test(value)||value===""){
return <input type="text" onChange={this.handleChange} placeholder="只能填字母"/>
}
throw new Error('出錯了!');
}
}
App.js
<ErrorBoundary>
<Example />
</ErrorBoundary>
方式二:static getDerivedStateFromError()
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
//顯示降級UI
return <div>出錯了bro!</div>
}
return this.props.children;
}
}
你也可以同時使用兩種。
看樣子componentDidCatch()
好像沒什么用,因為一般情況下瀏覽器會告訴你只能在控制台看錯誤日志,並且在沒有使用該錯誤邊界組件的情況下,控制台也會打印出同樣的錯誤信息。相比之下,能渲染出備用組件的static getDerivedStateFromError()
的實用性高一點。其實,無論使用哪一種,它都起到了創建錯誤邊界組件的作用。
錯誤邊界組件的工作方式類似於 JavaScript 的
catch {}
,不同之處在於它只針對 React 組件。且只有 class 組件才可以成為錯誤邊界組件。注意,它無法捕獲其自身的錯誤。
如果沒有使用錯誤邊界會怎樣?
自 React 16 起,任何未被錯誤邊界捕獲的錯誤將會導致整個 React 組件樹被卸載。
經驗告訴我們,完全移除比保留錯誤UI更好。例如,在類似 Messenger 的產品中,把異常的 UI 展示給用戶可能會導致用戶將信息錯發給別人。
增加錯誤邊界能夠讓你在應用發生異常時提供更好的用戶體驗。例如,Facebook Messenger 將側邊欄、信息面板、聊天記錄以及信息輸入框包裝在單獨的錯誤邊界中。如果其中的某些 UI 組件崩潰,其余部分仍然能夠交互。
try/catch的缺陷
try
/ catch
很棒但它僅能用於命令式代碼:
try {
showButton();
} catch (error) {
// ...
}
然而,React 組件是聲明式,並且具體指出 什么 需要被渲染:
<Button />
為什么無法捕獲事件處理器的內部錯誤?
React 不需要錯誤邊界來捕獲事件處理器中的錯誤。與 render 方法和生命周期方法不同,事件處理器不會在渲染期間觸發。因此,如果它們拋出異常,React 仍然能夠知道需要在屏幕上顯示什么。如果你需要在事件處理器內部捕獲錯誤,使用普通的 JavaScript 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>捕獲到一個錯誤</h1>
}
return <button onClick={this.handleClick}>點擊</button>
}
}