为啥我不应该使用 catch() 来处理 React useEffect API 调用中的错误?

Posted

技术标签:

【中文标题】为啥我不应该使用 catch() 来处理 React useEffect API 调用中的错误?【英文标题】:Why shouldn't I use catch() to handle errors in React useEffect API calls?为什么我不应该使用 catch() 来处理 React useEffect API 调用中的错误? 【发布时间】:2021-05-05 04:54:54 【问题描述】:

在 React 文档的这个页面上:

https://reactjs.org/docs/faq-ajax.html

代码注释说...

注意:在这里处理错误而不是 catch() 块很重要,这样我们就不会从组件中的实际错误中吞下异常。

...关于处理fetch 之后的第二个.then 的第二个参数中的错误。文档中完整的 sn-p 是:

  useEffect(() => 
    fetch("https://api.example.com/items")
      .then(res => res.json())
      .then(
        (result) => 
          setIsLoaded(true);
          setItems(result);
        ,
        // Note: it's important to handle errors here
        // instead of a catch() block so that we don't swallow
        // exceptions from actual bugs in components.
        (error) => 
          setIsLoaded(true);
          setError(error);
        
      )
  , [])

它没有详细说明这个建议,而且我已经看到许多使用 catch 处理来自 API 调用的错误的工作 React 代码示例。我试过谷歌搜索,但找不到任何澄清。

谁能给我更详细的解释,为什么我不应该使用catch 来处理在useEffect 挂钩中进行fetch API 调用时出现的错误?

或者在某些情况下可以这样做,但在其他情况下不可以?

“吞下组件中的异常 [...]”是什么意思?

此规则/指南适用于所有 React,还是仅适用于 API 调用?

或者只是到useEffect钩子或componentDidMount生命周期方法?

【问题讨论】:

【参考方案1】:

我能找到的所有内容似乎都链接回 this github issue 大约 2016 年。我会从那里逐字引用,因为它之前似乎没有被 Stack Overflow 报道过,而且它非常彻底地解释了事情:


.then(() => 
  this.setState( loaded: true )
)
.catch(()=>  
  console.log('Swallowed!') 
);

您的catch() 处理程序将捕获then() 中抛出的任何错误 它之前的链,包括由 render() 引起的链,由于 setState() 来电。

即使不直接使用setState,也可能遇到同样的问题 如果您调用的其他代码使用它(例如,Redux dispatch())。

如果您不想捕获由setState() 导致的错误,并且想要 只捕获网络故障(让我们想象一下你的Promise.resolve() 实际上是一个fetch()),你要使用第二个then()参数 而是:

componentDidMount() 
  Promise.resolve()
  .then(() => 
      this.setState( loaded: true )
  , (err) => 
    console.log('An error occurred (but not in setState!)', err);
  );

在这种情况下,除非你在链的后面catch(),否则错误 render() 将不会被捕获,并且具有良好的 Promise polyfill(或 Chrome 和其他浏览器中的原生 Promise),显示。


编辑:根据@Martin 的回答我去测试了这个,我可以确认这似乎不再是一个相关的问题。从 setState 开始的渲染错误在 v16.0 以后的任何版本的 React 中都不会被捕获,并且由于 useState 仅在 v16.8 中引入,因此这似乎不可能成为钩子的问题。

这里是 codesandbox,它演示了旧版本 React 中的原始问题。

【讨论】:

感谢您回答我的问题并测试和确认其他答案。【参考方案2】:

这是示例作者的复制粘贴错误。 第一个示例使用基于类的组件的组件状态:this.setState() 导致同步重新渲染(或者至少 2016 年的 react v15.0 就是这种情况,不确定今天是否成立)。因此,警告注释和对.then(onResolve, onReject) 的第二个参数的使用是合理的。 第二个示例使用函数组件的useState 钩子:setState 导致没有同步重新渲染,只有异步重新渲染。因此,警告注释和对.then(onResolve, onReject) 的第二个参数的使用是合理的。

为了说明这一点:使用useState 挂钩,您可以调用多个更新器,它们都只会在一次重新渲染中生效。

const [a, setA] = useState();
const [b, setB] = useState();
const [c, setC] = useState();

const updateState = () => 
  setA('foo');
  setB('bar');
  setC('buz');
  // will only cause one single re-render

因此使用 catch 完全没问题

useEffect(() => 
    fetch("https://api.example.com/items")
      .then(res => res.json())
      .then(
        (result) => 
          setIsLoaded(true);
          setItems(result);
        
      )
      .catch(
        (error) => 
          setIsLoaded(true);
          setError(error);
        
      )
  , [])

【讨论】:

所以如果我理解正确的话,那条注释是错误地留在示例中的,我绝对没有理由不使用 catch() 来处理 React useEffect API 调用中的错误。太好了,谢谢。 是的。您可以使用.catch(),我认为它可以提高代码的清晰度。

以上是关于为啥我不应该使用 catch() 来处理 React useEffect API 调用中的错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥不建议用 try catch

为啥 iOS 开发中很少用到 try catch 语句

当它应该表现得像 try/catch/finally 时,为啥使用会抛出异常?

为啥我不应该将 JSF SessionScoped bean 用于逻辑?

为啥使用 NSError 间接参数而不是 @try/@catch/@finally

为啥我不应该使用默认的 java 安全管理器 [重复]