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