为啥我不应该使用 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
,也可能遇到同样的问题 如果您调用的其他代码使用它(例如,Reduxdispatch()
)。如果您不想捕获由
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/finally 时,为啥使用会抛出异常?
为啥我不应该将 JSF SessionScoped bean 用于逻辑?