使用 Error Boundary 构建容错 React 应用(Error Boundary 最佳实践)
Read in English
本文将展示如何利用 error boundary 的最佳实践来构建一个健壮且容错的 React 应用,包括:何时使用 Error Boundary、构建带有错误处理和错误上报功能的 Error Boundary HOC。
核心概念
容错性
提高系统可靠性的一种常见做法是使系统具备容错性。这意味着即使用户、系统某些部分或上游服务产生错误,你的系统也必须保持弹性。
最小化错误影响
使系统具备容错性的一种方式是最小化错误的影响范围。如果某个模块发生错误,我们不希望它影响到其他模块。
Error Boundary
React 提供了 <ErrorBoundary /> 来处理组件抛出的错误。目前,大多数应用只在应用层级(最高层)使用它来捕获和处理错误。这意味着一旦应用中的任何组件抛出错误,整个应用都会崩溃。
在一个不重要的筛选组件中抛出错误,导致整个应用崩溃。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function App() { return ( <div className="App"> <ErrorBoundary> <Home /> </ErrorBoundary> </div> ); }
function Home() { return ( <div className="Home"> <ComponentA /> <ComponentB /> </div> ); }
function ComponentA() { throw new Error('error'); }
|
上面的代码会导致整个应用崩溃!
Remix:路由级别的 Error Boundary
Remix 提供了一种在路由级别处理错误的方式,应用的每个部分都是嵌套路由,我们可以使用 <ErrorBoundary /> 为每个路由处理错误。
没有路由级别的 error boundary,错误会导致整个应用崩溃。
有了路由级别的 error boundary,错误只会导致该路由崩溃。
何时使用 Error Boundary 的通用实践
为每个组件都添加 ErrorBoundary 是不可行的,这会大幅增加代码复杂性。我们需要做出权衡,在代码可维护性和错误处理范围之间取得平衡。
功能级别的错误处理
我们希望最小化错误的影响范围。然而,某些组件协同工作以形成完整的使用流程。例如,表单的不同部分,表单内部任意部分出现问题。保持表单其他部分继续工作是没有意义的。因此,我们在表单级别添加 Error Boundary,这样表单任何部分的崩溃都不会影响其他功能(如表格、搜索等)。
组件级别的错误处理
某些组件确实很容易产生错误,而它们本身并不是非常重要的功能。在这种情况下,我们也希望为这些组件添加组件级别的错误处理,例如表格中的筛选、搜索功能。以下情况下组件容易出错:
- 组件处理来自网络请求的数据,这些数据可能格式意外。
- 组件处理来自用户输入的数据,这些数据也可能格式意外。
- 组件具有非常复杂的业务逻辑和状态变更。
withErrorBoundary HOC(错误处理与错误上报)
为了降低添加组件级别错误处理的成本,我们可以创建一个 HOC 来为组件包装错误处理和错误上报功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import { ErrorBoundary } from 'react-error-boundary'; import { sendEvent } from '@/libs/event'; import { IconAlertCircle } from '@/components/icons';
export default function withErrorBoundary<T>(WrappedComponent: React.FC<T>, text?: string) { return (props: T) => ( ( <ErrorBoundary FallbackComponent={() => ( <div className='flex items-center gap-2'> <IconAlertCircle style={{ color: 'red' }} /> {text} </div> )} onError={error => { sendEvent({ name: 'component_error', content: `${WrappedComponent.name} error: ${error?.message}`, }); }} > {<WrappedComponent {...props} />} </ErrorBoundary> )); }
|
1 2
| export default withErrorBoundary<ComponentAProps>(ComponentA);
|
功能特性:
- 错误处理与降级展示:捕获错误并向用户展示降级内容(可自定义),而不影响其他模块。
- 错误上报:将带有组件名称和错误信息的日志上报到 slardar,以便调试。
error-boundary, error-handling, fault-tolerance, frontend, react, reliability, robustness — May 6, 2024