getDerivedStateFromError与componentDidCatch有何不同?

68

我从这里了解到:

componentDidCatch

  • 在浏览器中总是被调用
  • 在 DOM 已经更新的“提交阶段”被调用
  • 应该用于类似于错误报告的事情

getDerivedStateFromError

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

不过,我有些困惑:

  1. 它们是否都捕获相同类型的错误?还是每个生命周期将捕获不同的错误?
  2. 我是否应该始终同时使用它们(可能在同一个“错误捕获”组件中)?
  3. “使用 componentDidCatch 进行错误恢复并不理想,因为它强制回退 UI 总是同步渲染”那有什么问题吗?
3个回答

46

问题中的大部分陈述是正确的。目前,错误边界在SSR中不受支持,getDerivedStateFromErrorcomponentDidCatch不会影响服务器端。

它们是否都捕获相同类型的错误?还是每个生命周期将捕获不同的错误?

它们捕获相同的错误,但在不同的阶段。之前只能使用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 来说不是一个大问题,因为它可以被视为边缘情况。

1
好的,已更新答案。 - Estus Flask
1
OP中的一部分信息是不正确的(Reddit主题中链接的信息也是如此)。getDerivedStateFromError在服务器端渲染期间不会被调用。我已经在React 16.4和16.7中进行了测试。React文档https://reactjs.org/docs/error-boundaries.html指出:“错误边界不会捕获...服务器端渲染的错误”。 - Overlook Motel
1
https://www.npmjs.com/package/react-ssr-error-boundary 是一个解决方法,使得错误边界在服务器上工作,并且可以在新的 React 异步渲染器发布之前使用。 - Overlook Motel
这是否意味着,如果我想在现有的UI上显示一个toast消息,比如“没有网络连接”,我只需要使用componentDidCatch - Jonathan
有人能解释一下为什么getDerivedStateFromError是静态的吗?如果状态不是静态的,它如何能够返回新的状态?非静态字段不能被静态方法访问,对吗? - steak_Overcooked
显示剩余3条评论

19

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

根据 React 文档

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


它们是否捕获相同类型的错误?还是每个生命周期将捕获不同的错误?

这两个生命周期方法都将捕获相同的错误,但它们的参数不同。

虽然 getDerivedStateFromError 只接收错误作为参数,但 componentDidCatch 还会接收第二个参数: 信息对象info,其中包含有关抛出错误的组件的信息(componentStack)

getDerivedStateFromError() 在 "render" 阶段被调用,因此不允许使用 side-effect。如果需要这样的 use cases,请改用 componentDidCatch()。虽然 componentDidCatch 也可用于 setState,但在未来版本中将被弃用。

componentDidCatch 应该用于像记录错误这样的副作用。


此外,@Brian Vaughn 在提供的链接中详细阐述了它们的使用。

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

渲染阶段恢复更安全。 通过 componentDidCatch 进行错误恢复的故事有点勉强,因为它依赖于“null”在出错的组件下面的所有内容的中间提交。这可能导致在树中更高级别的任何实现 componentDidMount 或 componentDidUpdate 的组件内部发生后续错误,并且只是假设它们的引用将非空(因为在非错误情况下它们始终如此)。

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

如果出现错误,您的错误边界将首先调用getDerivedStateFromError()方法(以更新状态),然后调用render()方法(实际渲染备用UI),最后调用componentDidCatch (一旦备用UI已提交到DOM)。
如果您的错误边界定义了其他生命周期方法(例如componentWillUpdate、componentDidUpdate),它们也会被调用,就像在任何其他渲染时一样。
引用中说“使用componentDidCatch进行错误恢复并不理想,因为它会强制备用UI始终同步渲染”,这有什么问题吗?
这意味着,componentDidCatch在渲染备用UI的render方法之后被调用,可能会导致更多问题,而getDerivedStateFromError在渲染阶段之前更新状态,以便渲染正确的备用UI,并且不会对渲染的组件造成更多错误。此外,新版本的异步渲染可能会与目前的方法存在问题。

使用componentDidCatch进行错误恢复并不是最佳选择,因为它会强制回退UI始终同步渲染。我不完全理解它将“同步渲染”,这有什么问题吗? - Tomasz Mularczyk
@TomaszMularczyk,它的意思是,componentDidCatch在呈现回退UI的render方法之后被调用,这可能会导致更多问题,而getDerivedStateFromError在渲染阶段之前更新状态,以便正确的回退UI被呈现,并且不会对呈现的组件造成更多错误。此外,新版本旨在实现异步渲染,这可能会对当前方法造成问题。 - Shubham Khatri

6
实际上,它们都有相同的目标,但处于不同的阶段。当写ErrorBoundary组件时,我使用getDerivedStateFromError方法,因为我遵循了ReactJs文档中的规定。文档中有这样一句话:

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

肯定有一些原因,所以我总是使用getDerivedStateFromError来呈现回退UI,并使用componentDidCatch来捕获信息并执行操作。

这个例子有点不对,因为它在 componentDidCatch 中设置状态而不是在 getDerivedStateFromError 中设置。谢谢。 - tonix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接