我做了一个没有正常运行的反应应用程序,并且使用它的人注意到偶尔会发生一些奇怪的错误。我不知道为什么或发生了什么,并且无法重现它。
所以我想知道是否有办法将整个应用程序或部分应用程序包装在try / catch块中,以便我可以将错误发送到服务器上的错误日志中?
到目前为止,我所读到的只是你可以将整个渲染函数包装在try / catch中,但是由于用户的交互不能捕获任何错误吗?
答案 0 :(得分:30)
这就是我最终使用的
编辑:React 16介绍了正确的方法,请参阅@goldylucks答案。
componentWillMount: function ()
{
this.startErrorLog();
}
startErrorLog:function()
{
window.onerror = (message,file,line,column,errorObject) =>
{
column = column || (window.event && window.event.errorCharacter);
var stack = errorObject ? errorObject.stack : null;
//trying to get stack from IE
if(!stack)
{
var stack = [];
var f = arguments.callee.caller;
while (f)
{
stack.push(f.name);
f = f.caller;
}
errorObject['stack'] = stack;
}
var data = {
message:message,
file:file,
line:line,
column:column,
errorStack:stack,
};
//here I make a call to the server to log the error
//the error can still be triggered as usual, we just wanted to know what's happening on the client side
return false;
}
}
答案 1 :(得分:22)
React 16引入了Error Boundaries和componentDidCatch lifecycle method:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
然后你可以将它用作常规组件:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
或者您可以使用npm包react-error-boundary包装根组件,并设置回退组件和行为。
import ErrorBoundary from 'react-error-boundary';
const myErrorHandler = (error: Error, componentStack: string) => {
// ...
};
<ErrorBoundary onError={myErrorHandler}>
<ComponentThatMayError />
</ErrorBoundary>
答案 2 :(得分:15)
您可以利用React的BatchingStrategy API轻松地将try/catch
包装在所有React代码周围。超过window.onerror
的好处是,您可以在所有浏览器中获得良好的堆栈跟踪。即使像Microsoft Edge和Safari这样的现代浏览器也不会向window.onerror
提供堆栈跟踪。
这里是React 15.4的样子:
import ReactUpdates from "react-dom/lib/ReactUpdates";
import ReactDefaultBatchingStrategy from "react-dom/lib/ReactDefaultBatchingStrategy";
let isHandlingError = false;
const ReactTryCatchBatchingStrategy = {
// this is part of the BatchingStrategy API. simply pass along
// what the default batching strategy would do.
get isBatchingUpdates () { return ReactDefaultBatchingStrategy.isBatchingUpdates; },
batchedUpdates (...args) {
try {
ReactDefaultBatchingStrategy.batchedUpdates(...args);
} catch (e) {
if (isHandlingError) {
// our error handling code threw an error. just throw now
throw e;
}
isHandlingError = true;
try {
// dispatch redux action notifying the app that an error occurred.
// replace this with whatever error handling logic you like.
store.dispatch(appTriggeredError(e));
} finally {
isHandlingError = false;
}
}
},
};
ReactUpdates.injection.injectBatchingStrategy(ReactTryCatchBatchingStrategy);
此处完整撰写:https://engineering.classdojo.com/blog/2016/12/10/catching-react-errors/
答案 3 :(得分:3)
Error boundaries
太有限,不能捕获所有错误。
在 React 17 中,捕获所有错误,例如:
on click
),undefined exception
等同步异常您需要两个全局处理程序:
// TypeScript
export function registerHandlers(store: Store) {
window.addEventListener("error", (event) => {
store.dispatch<any>(setErrorAction({ message: event.message }));
});
window.addEventListener("unhandledrejection", (event: PromiseRejectionEvent) => {
store.dispatch<any>(setErrorAction({ message: event.reason.message }));
});
}
在 Redux Store
创建后调用它,结果,所有异常都将传递给 Redux,因此您可以 useSelector
获取它并在某处显示或记录(例如发送到服务器以获取存储)。
为了更好地覆盖 HTTP 错误,您可以在 Axios Response Interceptor
上捕获它们并从那里推送到存储(您将获得有关错误的更多信息)。只需在 unhandledrejection
(未处理的承诺异常)上进行适配或在拦截器中吞下它,这样它就不会翻倍。
答案 4 :(得分:2)
我遇到了同样的问题。我创建了一个Office应用程序,我既没有调试控制台也没有开发人员工具,所以我找不到发生错误的位置。
我创建了一个捕获所有console
个消息的单个组件(es6级),将消息保存到一个单独的数组中并称为“真正的”console
函数。
log(message) {
const msg = new Log(message);
this.pushMessage(msg);
this._target.log(message);
}
其中Log
是一个包含message
且type
和this._target
的简单包装,是window.console
的引用。所以我对info
,warn
和error
做了同样的事情。
此外,我创建了一个方法handleThrownErrors(message, url, lineNumber)
来捕获异常。
window.onerror = this.handleThrownErrors.bind(this);
至少我创建了一个类的实例(我称之为LogCollector
)并将其附加到窗口。
window.logCollector = new LogCollector();
现在我创建了一个react组件,它将logCollector实例(window.logCollector
)作为属性。反应组件定期检查收集的消息并在屏幕上显示。
componentDidMount() {
this.setInterval(this._forceUpdate, 500);
},
_forceUpdate() {
this.setState({update: !this.state.update});
}
this.setInterval()
是一个自己的函数,只需调用window.setInterval()
。
在render()
方法中:
return (
<div class="console">
{this.props.logCollector.getMessages().map(this.createConsoleMessage)}
</div>
);
注意:在所有其他文件之前加入
LogCollector
非常重要。注意:以上解决方案为非常简化的版本。例如:您可以通过添加自定义(消息)侦听器或捕获
404 Not found
错误(对于js-scripts和css-files)来改进它。