Javascript / React window.onerror 被触发两次

fan*_*ncy 10 html javascript reactjs

我想在我的 React 应用程序中全局捕获错误。

但是每次错误被捕获/转发两次到我注册的函数。

示例代码:

window.onerror = (msg, url, lineNo, columnNo, error) => {
console.log(msg)
  alert(msg)
}

class TodoApp extends React.Component {
  constructor(props) {
    super(props)


  }

  render() {

    return (
      <button onClick={(e)=>{
        console.log("clicked")
        null.bla
      }}>
        Create an error

      </button>
    )
  }
}

ReactDOM.render(<TodoApp />, document.querySelector("#app"))
Run Code Online (Sandbox Code Playgroud)

这是一个 JS 小提琴:https : //jsfiddle.net/dmxur0rc/4/

控制台只显示一个“点击”日志,因此触发两次的不是按钮,而是错误事件。

Pav*_*vel 7

知反应错误,与执行错误的边界有关。


小智 6

我找到了一个基本的解决方案,它应该适用于所有场景。

事实证明,该对象在所有调用中都是相同的,您可以设置一些内容以完全匹配它们,或者您可以将自定义属性附加到错误对象...

诚然,这可能仅适用于 window.addEventListener('error', function...),因为您将获得真正的错误对象作为参数,而不是 window.onerror = function... 获取数据部分,例如作为消息和行号,而不是真正的错误。

这基本上是我使用它的方式:

window.addEventListener('error', function (event) {
  if (event.error.hasBeenCaught !== undefined){
    return false
  }
  event.error.hasBeenCaught = true
  // ... your useful code here
})
Run Code Online (Sandbox Code Playgroud)

如果两次调用它时出现相同的错误,它将在获取有用代码之前退出,每个错误只执行一次有用代码。


Dav*_*ton 5

您需要true从错误处理程序返回,否则将触发默认错误处理程序:

https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror

当函数返回 true 时,这会阻止触发默认事件处理程序。

另请注意,其他错误处理程序可能通过addEventHandler.


Mon*_*nor 5

正如其他答案中提到的,问题出在 DEV 模式下的 React 中。在此模式下,它会重新抛出所有异常以“改善调试体验”。

我看到 4 种不同的错误情况

  1. 正常的 JS 错误(例如,来自事件处理程序,如问题中所示)。

    这些由 React 的发送window.onerror 两次invokeGuardedCallbackDev

  2. 期间发生的JS错误,并且组件树中render没有React的错误边界。

    与场景1相同。

  3. 期间发生的 JS 错误,并且组件树中的某处render存在错误边界。

    这些被发送到一次window.onerror invokeGuardedCallbackDev但也被错误边界的捕获 componentDidCatch

  4. 承诺内的 JS 错误未得到处理。

    这些不是发送到window.onerror,而是发送到window.onunhandledrejection。而且这种情况只会发生一次,所以这没有问题。

我的解决方法

window.addEventListener('error', function (event) {
    const { error } = event;
    // Skip the first error, it is always irrelevant in the DEV mode.
    if (error.stack?.indexOf('invokeGuardedCallbackDev') >= 0 && !error.alreadySeen) {
        error.alreadySeen = true;
        event.preventDefault();
        return;
    }
    // Normal error handling.
}, { capture: true });
Run Code Online (Sandbox Code Playgroud)