你能用try / catch块捕获React.js应用程序的所有错误吗?

时间:2015-06-29 09:08:36

标签: try-catch reactjs

我做了一个没有正常运行的反应应用程序,并且使用它的人注意到偶尔会发生一些奇怪的错误。我不知道为什么或发生了什么,并且无法重现它。

所以我想知道是否有办法将整个应用程序或部分应用程序包装在try / catch块中,以便我可以将错误发送到服务器上的错误日志中?

到目前为止,我所读到的只是你可以将整个渲染函数包装在try / catch中,但是由于用户的交互不能捕获任何错误吗?

5 个答案:

答案 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 BoundariescomponentDidCatch 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是一个包含messagetypethis._target的简单包装,是window.console的引用。所以我对infowarnerror做了同样的事情。

此外,我创建了一个方法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)来改进它。