Open pfan123 opened 3 years ago
React 项目中,很常见遇到页面由于某个 React 组件渲染错误(代码书写错误不规范或后端接口字段调整出错),导致整个应用被挂载出现白屏,且可能无法追踪造成影响极大,究其原因觉得是React 设计的坑点 自 React 16 起,任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载。 针对此类问题,我们如何来进行感知上报以及应急兜底呢🤔️?
React
Error Boundaries 是 React16 提出来用来捕获渲染时错误的概念,Error Boundaries 是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,且会渲染出兜底 UI。
Error Boundaries
Error Boundaries 可以用来捕获渲染时错误,API 如下:
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; } }
static getDerivedStateFromError
state
fallback
componentDidCatch
这两种方法中任意一个被定义时,这个组件就会成为 Error Boundaries 组件,可以阻止子组件渲染时报错。
错误边界的工作方式类似于 JavaScript 的 catch {},不同的地方在于错误边界只针对 React 组件。只有 class 组件才可以成为错误边界组件。
JavaScript
catch {}
class
建议将 Error Boundary 单独作为一个组件,而不是将错误监听方法与业务组件耦合,一方面考虑到复用,另一方面则因错误检测只对子组件生效。
Error Boundary
<ErrorBoundary> <MyWidget /> </ErrorBoundary>
注意: 错误边界仅可以捕获其子组件的错误,无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会冒泡至最近的上层错误边界,这也类似于 JavaScript 中 catch {} 的工作机制。
注意
React 16 定义和使用错误边界的例子。
React Error Boundaries 官方文档 里提到了四种无法 Catch 的错误场景:
回调事件,由于回调事件执行时机不在渲染周期内,因此无法被 Error Boundaries Catch 住,如有必要得自行 try/catch
Error Boundaries Catch
try/catch
异步代码,比如 setTimeout 或 requestAnimationFrame,和第一条同理
setTimeout
requestAnimationFrame
服务端渲染
Error Boundaries 组件自身触发的错误,只能捕获其子组件的错误
这也是使用 Error Boundaries 最容易有疑问的地方。
对于不能捕获到的错误情况, 是因为 getDerivedStateFromError 执行在 render 阶段,componentDidCatch 执行在 commit 阶段,过了这两个阶段(即一次渲染周期)就无法捕获到。
getDerivedStateFromError
render
commit
无法捕获编译时错误 React 官方 API Error Boundaries 也只能捕获运行时错误,而对编译时错误无能为力。 编译时错误包括不限于编译环境错误、运行前的框架错误检查提示、TS/Flow 类型错误等,这些都是 Error Boundaries 无法捕获的,且没有更好的办法 Catch 住,遇到编译错误就在编译时解决吧,仅关注运行时错误就好了。
无法捕获编译时错误
React 官方 API Error Boundaries 也只能捕获运行时错误,而对编译时错误无能为力。
编译时错误包括不限于编译环境错误、运行前的框架错误检查提示、TS/Flow 类型错误等,这些都是 Error Boundaries 无法捕获的,且没有更好的办法 Catch 住,遇到编译错误就在编译时解决吧,仅关注运行时错误就好了。
TS/Flow
Catch
虽然函数式组件无法定义 Error Boundaries,但 Error Boundaries 可以捕获函数式组件的异常错误:
// ErrorBoundary 组件 class ErrorBoundary extends React.Component { // ... } // Hooks 函数组件 const Child = (props) => { React.useEffect(() => { console.log(1); props.a.b; console.log(2); }, [props.a.b]); return <div />; }; // 可以捕获所有组件异常,包括 Function Component 的子组件 const App = () => { return ( <ErrorBoundary> <Child /> </ErrorBoundary> ); };
注意:出现在 deps 中的错误会立即被 Catch,导致 console.log(1) 都无法打印。但如果是下面的代码,则可以打印出 console.log(1),无法打印出 console.log(2):
deps
console.log(1)
console.log(2)
const Child = (props) => { React.useEffect(() => { console.log(1); props.a.b; console.log(2); }, []); return <div />; };
所以 React 官网的这句话并不是指 Error Boundaries 对 Hooks 不生效,而是指 Error Boundaries 无法以 Hooks 方式指定,对功能是没有影响的:
Hooks
getSnapshotBeforeUpdate, componentDidCatch and getDerivedStateFromError: There are no Hook equivalents for these methods yet, but they will be added soon.
getSnapshotBeforeUpdate
Error Boundaries 可以捕获所有子元素渲染时异常,包括 render、各生命周期函数,但也有很多使用限制,我们需要正确使用它。
Error Boundaries 也不是万能的,更多时候我们要避免并及时修复错误以及错误兜底降低影响,并在第一时间内监控起来并快速修复。
static-getderivedstatefromerror
componentdidcatch
use-error-boundary
React Lifecycle Methods diagram
前言
React
项目中,很常见遇到页面由于某个React
组件渲染错误(代码书写错误不规范或后端接口字段调整出错),导致整个应用被挂载出现白屏,且可能无法追踪造成影响极大,究其原因觉得是React
设计的坑点 自 React 16 起,任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载。 针对此类问题,我们如何来进行感知上报以及应急兜底呢🤔️?Error Boundaries
是 React16 提出来用来捕获渲染时错误的概念,Error Boundaries
是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,且会渲染出兜底 UI。Error Boundaries
Error Boundaries
可以用来捕获渲染时错误,API 如下:static getDerivedStateFromError
:在出错后有机会修改state
触发最后一次错误fallback
的渲染。componentDidCatch
:用于出错时副作用代码,比如错误上报等。这两种方法中任意一个被定义时,这个组件就会成为
Error Boundaries
组件,可以阻止子组件渲染时报错。错误边界的工作方式类似于
JavaScript
的catch {}
,不同的地方在于错误边界只针对React
组件。只有class
组件才可以成为错误边界组件。建议将
Error Boundary
单独作为一个组件,而不是将错误监听方法与业务组件耦合,一方面考虑到复用,另一方面则因错误检测只对子组件生效。注意
: 错误边界仅可以捕获其子组件的错误,无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会冒泡至最近的上层错误边界,这也类似于JavaScript
中catch {}
的工作机制。Error Boundaries 无法捕获错误
React Error Boundaries 官方文档 里提到了四种无法 Catch 的错误场景:
回调事件,由于回调事件执行时机不在渲染周期内,因此无法被
Error Boundaries Catch
住,如有必要得自行try/catch
异步代码,比如
setTimeout
或requestAnimationFrame
,和第一条同理服务端渲染
Error Boundaries
组件自身触发的错误,只能捕获其子组件的错误这也是使用
Error Boundaries
最容易有疑问的地方。对于不能捕获到的错误情况, 是因为
getDerivedStateFromError
执行在render
阶段,componentDidCatch
执行在commit
阶段,过了这两个阶段(即一次渲染周期)就无法捕获到。Error Boundaries 可作用于 Function Component
虽然函数式组件无法定义
Error Boundaries
,但Error Boundaries
可以捕获函数式组件的异常错误:注意
:出现在deps
中的错误会立即被Catch
,导致console.log(1)
都无法打印。但如果是下面的代码,则可以打印出console.log(1)
,无法打印出console.log(2)
:所以
React
官网的这句话并不是指Error Boundaries
对Hooks
不生效,而是指Error Boundaries
无法以Hooks
方式指定,对功能是没有影响的:总结
Error Boundaries
可以捕获所有子元素渲染时异常,包括render
、各生命周期函数,但也有很多使用限制,我们需要正确使用它。Error Boundaries
也不是万能的,更多时候我们要避免并及时修复错误以及错误兜底降低影响,并在第一时间内监控起来并快速修复。Other Resources
Error Boundaries
static-getderivedstatefromerror
componentdidcatch
use-error-boundary
React Lifecycle Methods diagram