一张图概括:
这里简略解说下 React 工作流程,后文有用。分为三步:
触发更新
render 阶段:计算更新会造成的副作用
commit 阶段:在宿主环境执行副作用
副作用有很多,比方:
插入 DOM 节点
执行 useEffect 回调
好了,让咱们进入主题。
什么是 Error Boundaries
React 提供了两个与错误处理相干的 API:
getDerivedStateFromError:静态方法,当谬误产生后提供一个机会渲染 fallback UI
componentDidCatch:组件实例办法,当谬误产生后提供一个机会记录错误信息
应用了这两个 API 的 ClassComponent 通常被称为 Error Boundaries(谬误边界)。
在 Error Boundaries 的子孙组件中产生的所有 React 工作流程内的谬误都会被 Error Boundaries 捕捉。
通过开篇的介绍能够晓得,React 工作流程指:
render 阶段
commit 阶段
思考如下代码:
class ErrorBoundary extends Component {
componentDidCatch(e) {
console.warn(“产生谬误”, e);
}
render() {
return <div>{this.props.children}</div>;
}
}
const App = () => (
<ErrorBoundary>
<A><B/></A>
<C/>
<ErrorBoundary>
)
A、B、C 作为 ErrorBoundary 的子孙组件,当产生 React 工作流程内的谬误,都会被 ErrorBoundary 中的 componentDidCatch 办法捕捉。
步骤 1: 捕捉谬误
首先来看工作流程中的谬误都是何时被捕捉的。
render 阶段的外围代码如下,产生的谬误会被 handleError 解决:
do {
try {
// 对于并发更新则是 workLoopConcurrent
workLoopSync();
break;
} catch (thrownValue) {
handleError(root, thrownValue);
}
} while (true);
commit 阶段蕴含很多工作,比方:
componentDidMount/Update 执行
绑定 / 解绑 ref
useEffect/useLayoutEffectcallback 与 destroy 执行
这些工作会以如下模式执行,产生的谬误被 captureCommitPhaseError 解决:
try {
// …执行某项工作
} catch (error) {
captureCommitPhaseError(fiber, fiber.return, error);
}
步骤 2: 结构 callback
能够发现,即便没有 Error Boundaries,工作流程中的谬误曾经被 React 捕捉了。而正确的逻辑应该是:
如果存在 Error Boundaries,执行对应 API
抛出 React 的提示信息
如果不存在 Error Boundaries,抛出未捕捉的谬误
所以,不论是 handleError 还是 captureCommitPhaseError,都会从产生谬误的节点的父节点开始,逐层向上遍历,寻找最近的 Error Boundaries。
一旦找到,就会结构:
用于执行 Error Boundaries API 的 callback
用于抛出 React 提示信息的 callback
// … 为了可读性,逻辑有删减
function createClassErrorUpdate() {
if (typeof getDerivedStateFromError === ‘function’) {
// 用于执行 getDerivedStateFromError 的 callback
update.payload = () => {return getDerivedStateFromError(error);
};
// 用于抛出 React 提示信息的 callback
update.callback = () => {logCapturedError(fiber, errorInfo);
};
}
if (inst !== null && typeof inst.componentDidCatch === ‘function’) {
// 用于执行 componentDidCatch 的 callback
update.callback = function callback() {this.componentDidCatch(error);
};
}
return update;
}
如果没有找到 Error Boundaries,持续向上遍历直到根节点。
此时会结构:
用于抛出未捕捉谬误的 callback
用于抛出 React 提示信息的 callback
// … 为了可读性,逻辑有删减
funffction createRootErrorUpdate() {
// 用于抛出“未捕捉的谬误”及“React 的提示信息”的 callback
update.callback = () => {
onUncaughtError(error);
logCapturedError(fiber, errorInfo);
};
return update;
}
执行 callback
结构好的 callback 在什么时候执行呢?
在 React 中有两个执行用户自定义 callback 的 API:
对于 ClassComponent,this.setState(newState, callback)中 newState 和 callback 参数都能传递 Function 作为 callback
所以,对于 Error Boundaries,相当于被动触发了一次更新:
this.setState(() => {
// 用于执行 getDerivedStateFromError 的 callback
}, () => {
// 用于执行 componentDidCatch 的 callback
// 以及 用于抛出 React 提示信息的 callback
})