getDerivedStateFromError 和 componentDidCatch 有什么区别

IT技术 javascript reactjs
2021-04-14 18:16:52

我从这里了解到的

componentDidCatch

  • 总是在浏览器中调用
  • 当 DOM 已经更新时,在“提交阶段”期间调用
  • 应该用于诸如错误报告之类的事情

getDerivedStateFromError

  • 在服务器端渲染期间也被调用
  • 当 DOM 尚未更新时在“渲染阶段”调用
  • 应该用于呈现后备 UI

不过,我对一些事情有点困惑:

  1. 他们都捕捉到相同类型的错误吗?或者每个生命周期都会捕获不同的错误?
  2. 我应该总是使用两者(可能在同一个“错误捕获”组件中)?
  3. “使用 componentDidCatch 进行错误恢复不是最佳选择,因为它强制回退 UI 始终同步呈现”这有什么问题?
3个回答

问题中的陈述大部分是正确的。目前 SSR 不支持错误边界,getDerivedStateFromError并且componentDidCatch不影响服务器端。

他们都捕捉到相同类型的错误吗?或者每个生命周期都会捕获不同的错误?

他们在不同的阶段捕获相同的错误。这以前可以componentDidCatch单独使用:

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch() {
    this.setState({ hasError: true });
  }

做同样的事情,componentDidCatch在将异步渲染的支持添加到ReactDOMServer.

我应该总是使用两者(可能在同一个“错误捕获”组件中)?

可以同时使用两者。文档中的一个示例表明:

class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    logComponentStackToMyService(info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

在这种情况下,责任在他们之间划分。getDerivedStateFromError做唯一的好处,即在发生错误时更新状态,同时componentDidCatch提供副作用并this在需要时可以访问组件实例。

“使用 componentDidCatch 进行错误恢复不是最佳选择,因为它强制回退 UI 始终同步呈现”这有什么问题?

新的 React 版本旨在实现更高效的异步渲染。正如评论中也提到,同步渲染对于回退 UI 不是一个大问题,因为它可以被视为边缘情况。

这是否意味着如果我想在现有 UI 上显示 Toast 消息,说“没有网络连接”,我可以只使用componentDidCatch
2021-05-30 18:16:52
当然。更新了答案。
2021-06-06 18:16:52
好的!你也可以向我解释一下刚刚添加到问题中的第三点吗?
2021-06-12 18:16:52
npmjs.com/package/react-ssr-error-boundary是一种使错误边界在服务器上工作的变通方法,可以使用它直到今年晚些时候新的 React 异步渲染器出现。
2021-06-12 18:16:52
OP 中的一条信息不正确(它链接到的 Reddit 线程中的信息也是如此)。getDerivedStateFromError在服务器端渲染期间不调用。我已经在 React 16.4 和 16.7 中对此进行了测试。React docs reactjs.org/docs/error-boundaries.html状态“错误边界不会捕获错误...服务器端渲染”
2021-06-21 18:16:52

当渲染过程中、生命周期方法中或任何子组件的构造函数中出现错误时,都会调用这两种方法。它们可以在实现错误边界时使用

根据React 文档

getDerivedStateFromError在后代组件抛出错误后调用生命周期。它接收作为参数抛出的错误,并应该返回一个值来更新状态。


他们都捕捉到相同类型的错误吗?或者每个生命周期都会捕获不同的错误?

这两种生命周期方法都会捕获相同的错误,但是这两个组件的参数不同。

虽然getDerivedStateFromError只接收错误作为参数,componentDidCatch 还接收第二个参数,即info, i.e An object with a componentStack key containing information about which component threw the error.

getDerivedStateFromError()在“渲染”阶段调用,因此不允许出现副作用。对于这些用例,请componentDidCatch()改用。虽然componentDidCatch也可以用于 setState 但这将在未来的版本中被弃用

componentDidCatch 应该用于副作用,如记录错误


还在@Brian Vaughn您提供的链接上详细说明了它们的用法

getDerivedStateFromError 适用于服务器端渲染。 componentDidCatch 是一个提交阶段生命周期,但服务器上没有提交阶段。getDerivedStateFromError 是一个渲染阶段生命周期,因此它可用于在服务器上启用错误处理。

渲染阶段恢复更安全。错误恢复 via 的故事 componentDidCatch有点笨拙,因为它依赖于对错误组件下方的所有内容“null”的中间提交。这可能会导致在树中更高层的任何组件内部出现后续错误,这些组件实现了 componentDidMount 或 componentDidUpdate 并假设它们的 refs 将为非空(因为它们始终处于非错误情况下)。

getDerivedStateFromError不强制同步渲染因为来自提交阶段生命周期的状态更新始终是同步的,并且因为在提交阶段调用 componentDidCatch - 使用 componentDidCatch 进行错误恢复不是最佳选择,因为它强制回退 UI 始终同步呈现。(诚​​然,这不是一个大问题,因为错误恢复应该是一个边缘情况。)

如果发生错误,您的错误边界的 getDerivedStateFromError()方法将首先被调用(更新状态),然后是 render() 方法(实际呈现回退 UI),然后componentDidCatch(一旦回退 UI 已提交到 DOM) .

如果您的错误边界定义了其他生命周期方法(例如 componentWillUpdate、componentDidUpdate),它们也会被调用,就像它们在任何其他渲染上一样。


“使用 componentDidCatch 进行错误恢复不是最佳选择,因为它强制回退 UI 始终同步呈现”这有什么问题?

这意味着,在呈现回退 UI 的呈现方法之后调用 componentDidCatch,这可能会导致更多问题,而getDerivedStateFromError在呈现阶段之前更新状态,以便呈现正确的回退 UI,并且不会对呈现的组件造成更多错误。此外,新版本的目标是异步渲染,这可能与当前方法存在问题

“使用 componentDidCatch 进行错误恢复不是最佳选择,因为它强制回退 UI 始终同步呈现”。我不完全明白它会“同步渲染”是什么意思?我的意思是,这有什么问题?
2021-05-26 18:16:52
@TomaszMularczyk,它的意思是,componentDidCatch 在呈现回退 UI 的呈现方法之后调用,这可能会导致更多问题,而 getDerivedStateFromError 在呈现阶段之前更新状态,以便呈现正确的回退 UI 并且不会导致更多错误渲染的组件。此外,新版本的目标是异步渲染,这可能与当前方法存在问题
2021-06-05 18:16:52

实际上,他们都有相同的目标,但处于不同的阶段,当然,对于编写ErrorBoundary组件,我使用该getDerivedStateFromError方法是因为我遵守ReactJs Docs文档中有这样一句话:

getDerivedStateFromError()在抛出错误后,使用静态呈现回退 UI。使用componentDidCatch()记录错误信息。

当然,它有一些原因,所以为了呈现我经常使用的回退 UIgetDerivedStateFromError和捕捉信息并做一些我使用的事情componentDidCatch

codepen.io/gaearon/pen/wqvxGa那么这个例子有点错误,因为它设置状态componentDidCatch而不是设置状态getDerivedStateFromError 谢谢
2021-06-14 18:16:52