?
This document uses PHP Chinese website manual Release
過去,組件內(nèi)部的JavaScript錯(cuò)誤用于破壞React的內(nèi)部狀態(tài)并導(dǎo)致它在下次呈現(xiàn)時(shí)發(fā)出 隱蔽 錯(cuò)誤。這些錯(cuò)誤總是由應(yīng)用程序代碼中的早期錯(cuò)誤引起的,但React沒有提供在組件中正常處理它們的方法,并且無法從它們中恢復(fù)。
部分UI中的JavaScript錯(cuò)誤不應(yīng)該破壞整個(gè)應(yīng)用程序。為了解決React用戶的這個(gè)問題,React 16引入了一個(gè)“錯(cuò)誤邊界”的新概念。
錯(cuò)誤邊界是React組件,可以在其子組件樹中的任何位置捕獲JavaScript錯(cuò)誤,記錄這些錯(cuò)誤并顯示回退UI,而不是崩潰的組件樹。錯(cuò)誤邊界在渲染期間,生命周期方法以及整個(gè)樹下的構(gòu)造函數(shù)中捕獲錯(cuò)誤。
注意錯(cuò)誤邊界不會(huì)捕獲以下錯(cuò)誤:
事件處理程序(了解更多)
異步代碼(例如setTimeout
或requestAnimationFrame
回調(diào))
服務(wù)器端渲染
錯(cuò)誤邊界本身(而不是它的子項(xiàng))拋出的錯(cuò)誤
如果一個(gè)類組件定義了一個(gè)新的生命周期方法,它將成為一個(gè)錯(cuò)誤邊界componentDidCatch(error, info)
:
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } componentDidCatch(error, info) { // Display fallback UI this.setState({ hasError: true }); // You can also log the error to an error reporting service logErrorToMyService(error, info); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; }}
然后,您可以將其用作常規(guī)組件:
<ErrorBoundary> <MyWidget /></ErrorBoundary>
該componentDidCatch()
方法像JavaScript catch {}
塊一樣工作,但對(duì)于組件。只有類組件可能是錯(cuò)誤邊界。在實(shí)踐中,大多數(shù)時(shí)候您會(huì)想要聲明一個(gè)錯(cuò)誤邊界組件并在整個(gè)應(yīng)用程序中使用它。
請(qǐng)注意,錯(cuò)誤邊界只會(huì)在樹中的下面的組件中捕獲錯(cuò)誤。錯(cuò)誤邊界本身不能捕獲錯(cuò)誤。如果錯(cuò)誤邊界嘗試呈現(xiàn)錯(cuò)誤消息失敗,則錯(cuò)誤將傳播到其上方最接近的錯(cuò)誤邊界。這也與catch {}塊在JavaScript中的工作方式類似。
error
是一個(gè)已經(jīng)拋出的錯(cuò)誤。
info
是一個(gè)componentStack
關(guān)鍵的對(duì)象。在拋出錯(cuò)誤期間,該屬性具有關(guān)于組件堆棧的信息。
//...componentDidCatch(error, info) { /* Example stack information: in ComponentThatThrows (created by App) in ErrorBoundary (created by App) in div (created by App) in App */ logComponentStackToMyService(info.componentStack);}//...
聲明和使用錯(cuò)誤的邊界這個(gè)例子與陣營16測(cè)試版。
錯(cuò)誤界限的粒度取決于您。您可能會(huì)封裝頂級(jí)路由組件以向用戶顯示“出錯(cuò)了”消息,就像服務(wù)器端框架經(jīng)常處理崩潰一樣。您也可以將各個(gè)小部件封裝在錯(cuò)誤邊界內(nèi),以防止其崩潰應(yīng)用程序的其余部分。
這一變化具有重要意義。從React 16開始,沒有被任何錯(cuò)誤邊界捕獲的錯(cuò)誤將導(dǎo)致整個(gè)React組件樹的卸載。
我們辯論了這個(gè)決定,但根據(jù)我們的經(jīng)驗(yàn),離開損壞的用戶界面比徹底刪除它更糟糕。例如,像Messenger這樣的產(chǎn)品將可見的UI留下,可能會(huì)導(dǎo)致某人向錯(cuò)誤的人發(fā)送消息。同樣,支付應(yīng)用程序顯示錯(cuò)誤的數(shù)量比不呈現(xiàn)任何內(nèi)容更糟糕。
這一變化意味著,當(dāng)您遷移到React 16時(shí),您可能會(huì)發(fā)現(xiàn)以前未被注意到的應(yīng)用程序中現(xiàn)有的崩潰。當(dāng)出現(xiàn)錯(cuò)誤時(shí),添加錯(cuò)誤邊界可以讓您提供更好的用戶體驗(yàn)。
例如,F(xiàn)acebook Messenger將邊欄,信息面板,會(huì)話日志和消息輸入內(nèi)容封裝在單獨(dú)的錯(cuò)誤邊界中。如果其中一個(gè)UI區(qū)域中的某個(gè)組件發(fā)生崩潰,則其余組件保持互動(dòng)。
我們還鼓勵(lì)您使用JS錯(cuò)誤報(bào)告服務(wù)(或自己構(gòu)建),以便您可以了解在生產(chǎn)中發(fā)生的未處理異常并對(duì)其進(jìn)行修復(fù)。
即使應(yīng)用程序意外吞下它們,React 16也會(huì)將所有在渲染過程中發(fā)生的錯(cuò)誤打印到開發(fā)中的控制臺(tái)。除了錯(cuò)誤消息和JavaScript堆棧之外,它還提供組件堆棧跟蹤?,F(xiàn)在您可以看到組件樹中確切發(fā)生故障的位置:
您還可以在組件堆棧跟蹤中看到文件名和行號(hào)。這在默認(rèn)情況下在Create React App項(xiàng)目中起作用:
如果您不使用Create React App,則可以手動(dòng)將此插件添加到您的Babel配置中。請(qǐng)注意,它僅用于開發(fā),并且必須在生產(chǎn)中禁用。
注意堆棧跟蹤中顯示的組件名稱取決于
Function.name
屬性。如果您支持舊版瀏覽器和尚未提供此功能的設(shè)備(例如IE 11),請(qǐng)考慮Function.name
在您的捆綁應(yīng)用程序中包含一個(gè)polyfill,例如function.name-polyfill
。或者,您可以displayName
在所有組件上明確設(shè)置屬性。
try
/ catch
很好,但它只適用于命令式代碼:
try { showButton();} catch (error) { // ...}
然而,反應(yīng)的組分是聲明,并指定哪些應(yīng)該呈現(xiàn):
<Button />
錯(cuò)誤邊界保留了React的聲明性質(zhì),并按照您的預(yù)期行事。例如,即使樹中深處componentDidUpdate
的setState
某個(gè)鉤子發(fā)生錯(cuò)誤,它仍然會(huì)正確傳播到最近的錯(cuò)誤邊界。
錯(cuò)誤邊界不會(huì)在事件處理程序中捕獲錯(cuò)誤。
React不需要錯(cuò)誤邊界從事件處理程序中的錯(cuò)誤中恢復(fù)。與渲染方法和生命周期鉤子不同,事件處理程序在渲染過程中不會(huì)發(fā)生。所以如果他們拋出,React仍然知道要在屏幕上顯示什么。
如果您需要在事件處理程序中捕獲錯(cuò)誤,請(qǐng)使用常規(guī)的JavaScript try
/ catch
語句:
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { error: null }; } handleClick = () => { try { // Do something that could throw } catch (error) { this.setState({ error }); } } render() { if (this.state.error) { return <h1>Caught an error.</h1> } return <div onClick={this.handleClick}>Click Me</div> }}
請(qǐng)注意,上面的示例演示了常規(guī)的JavaScript行為并且不使用錯(cuò)誤邊界。
React 15在一個(gè)不同的方法名稱下包含了對(duì)錯(cuò)誤邊界的非常有限的支持:unstable_handleError
。此方法不再有效,您需要componentDidCatch
從第16個(gè)beta版本開始將其更改為代碼。
對(duì)于這個(gè)改變,我們提供了一個(gè)codemod來自動(dòng)遷移你的代碼。