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

Fli*_*ion 33 try-catch reactjs

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

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

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

Fli*_*ion 32

这就是我最终使用的东西

编辑: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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你只是把它放在根组件中吗? (2认同)

gol*_*cks 27

React 16引入了Error BoundariescomponentDidCatch生命周期方法:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以将它用作常规组件:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用npm包react-error-boundary包装根组件,并设置回退组件和行为.

import ErrorBoundary from 'react-error-boundary';

const myErrorHandler = (error: Error, componentStack: string) => {
  // ...
};

<ErrorBoundary onError={myErrorHandler}>
  <ComponentThatMayError />
</ErrorBoundary>
Run Code Online (Sandbox Code Playgroud)

  • 不,错误边界组件似乎无法捕获代码中抛出的 JS 错误——只能捕获渲染中的错误。 (3认同)
  • 否决,停止回答错误的答案,太奇怪了,这不是捕捉错误 (2认同)

Byr*_*ong 16

您可以利用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);
Run Code Online (Sandbox Code Playgroud)

完整的文章:https://engineering.classdojo.com/blog/2016/12/10/catching-react-errors/


and*_*fox 16

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 }));
  });
}
Run Code Online (Sandbox Code Playgroud)

Store在创建Redux 后调用此函数,所有异常都将传递给 Redux,因此您可以useSelector获取它并在某处显示或记录(例如发送到服务器进行存储)。

为了更好地覆盖 HTTP 错误,您可以捕获它们Axios Response Interceptor并从那里推送到存储(您将获得有关错误的更多信息)。只需将其安装在unhandledrejection(未处理的承诺异常)或吞入拦截器中,这样它就不会加倍。