我希望全局捕获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"))
这是一个JS小提琴: https://jsfiddle.net/dmxur0rc/4/
控制台只显示一个“点击”日志,因此它不是触发两次的按钮,而是错误事件。
答案 0 :(得分:3)
您需要从错误处理程序返回true
,否则将触发默认错误处理程序:
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror
当函数返回true时,这会阻止触发默认事件处理程序。
另请注意,其他错误处理程序可能会通过addEventHandler
生效。
答案 1 :(得分:0)
由于在JSFiddle上运行它的性质,它看起来可能会发射两次。在正常的构建过程中(使用webpack
和babel
)代码与此类脚本错误应该无法转换。
答案 2 :(得分:0)
它是known react error,与错误边界的实现有关。
答案 3 :(得分:0)
我找到了适用于所有情况的基本解决方案。
事实证明,该对象在所有调用中都是相同的,您可以设置一些东西使其完全匹配,或者可以将自定义属性附加到错误对象上...
诚然,这只能与window.addEventListener('error',function ...)一起使用,因为您将获得真正的错误对象作为参数,而不是window.onerror = function ...会获取数据部分,例如message和lineNumber而不是实际错误。
这基本上就是我的使用方式:
window.addEventListener('error', function (event) {
if (event.error.hasBeenCaught !== undefined){
return false
}
event.error.hasBeenCaught = true
// ... your useful code here
})
如果两次被相同的错误调用,它将在进入有用代码之前退出,每个错误仅执行一次有用代码。
答案 4 :(得分:0)
另一种方法是将上一个错误的消息存储在状态中,并检查第二次发生的时间。
export default MyComponent extends React.Component{
constructor(props){
super(props);
this.state = {message: null};
}
componentDidMount(){
const root = this;
window.onerror = function(msg, url, line, column, error){
if(root.state.message !== msg){
root.setState({message: msg});
// do rest of the logic
}
}
}
}
但是无论如何,最好使用React Error Boundaries。而且你可以 在错误内部实施此全局javascript错误处理 边界分量。哪里都可以捕获js错误(通过
window.onerror
)和React错误(使用componendDidCatch
)。
答案 5 :(得分:0)
我使用此错误边界来处理React和全局错误。这是来自React documentation的一些建议:
class ErrorBoundary extends React.Component {
state = {
error: null,
};
lastError = null;
// This lifecycle is invoked after an error has been thrown by a descendant component. It receives the error that was thrown as a parameter and should return a value to update state.
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return {
error,
};
}
componentDidMount() {
window.onerror = (msg, url, line, column, error) => {
this.logError({
error,
});
};
}
// getDerivedStateFromError() is called during the “render” phase, so side-effects are not permitted. For those use cases, use componentDidCatch() instead.
// This lifecycle is invoked after an error has been thrown by a descendant component. It receives two parameters:
// error - The error that was thrown.
// info - An object with a componentStack key containing
componentDidCatch(error, info) {
// avoid calling log error twice
if (this.lastError && this.lastError.message === this.state.error.message) {
return true;
}
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
// logComponentStackToMyService(info.componentStack);
this.logError({
error,
info,
});
}
async logError({
error,
info
}) {
this.lastError = error;
try {
await fetch('/error', {
method: 'post',
body: JSON.stringify(error),
});
} catch (e) {}
}
render() {
if (this.state.error) {
return display error ;
}
return this.props.children;
}
}
答案 6 :(得分:0)
正如其他答案中提到的,问题出在 DEV 模式下的 React 中。在这种模式下re-throws all exceptions可以“改善调试体验”。
正常的 JS 错误(例如,来自事件处理程序,如问题中所述)。
这些由 React 的 window.onerror
发送给 invokeGuardedCallbackDev
两次。
在 render
期间发生的 JS 错误,并且组件树中没有 React 的 error boundary。
与场景 1 相同。
在 render
期间发生的 JS 错误,并且在组件树的某处有一个错误边界。
这些被window.onerror
发送给invokeGuardedCallbackDev
一次,但也会被错误边界捕获{ {1}}。
Promise 中的 JS 错误,未处理。
这些不会发送到 componentDidCatch
,而是发送到 window.onerror
。而且这种情况只会发生一次,所以没问题。
window.onunhandledrejection