sentry-javascript icon indicating copy to clipboard operation
sentry-javascript copied to clipboard

feat(react): Add `Sentry.reactErrorHandler`

Open AbhiPrasad opened this issue 1 year ago • 1 comments

ref https://github.com/getsentry/sentry-javascript/issues/11798

React 19 brings some improvements to error handling with https://react.dev/blog/2024/04/25/react-19#error-handling! This PR exposes a new method, Sentry.captureReactException that helps users interact with these new React 19 error handling hooks.

Background

React Error handling originally relied only on error boundaries, React components that would automatically catch component rendering errors and display fallback UIs.

class ErrorBoundary extends React.Component {
  // ...

  public componentDidCatch(error: any, info: React.ErrorInfo): void {
    // Example "componentStack":
    //   in ComponentThatThrows (created by App)
    //   in ErrorBoundary (created by App)
    //   in div (created by App)
    //   in App
    logErrorToMyService(error, info.componentStack);
  }
}

We added support for this by exporting our own ErrorBoundary component.

If you notice above, the info parameter passed into componentDidCatch has a componentStack property. componentStack is a synthetic stacktrace that represents the component call-chain that threw the error. Fortunately because this is a synthetic stacktrace, we can parse it as an error in the SDK and attach it to the original exception as a linked exception via error.cause.

Important to note: If a component chain is not wrapped in an error boundary though, we do not get access to the componentStack, as the error bubbles up to the global error handler.

React 19

In React 19 they've two new options, onCaughtError and onUncaughtError to the React DOM public API.

onCaughtError is a callback called when React catches an error in an Error Boundary. This effectively works just like having an ErrorBoundary with componentDidCatch.

onUncaughtError is a callback called when an error is thrown and not caught by an Error Boundary. This means we can add componentStack information to these errors without requiring users to add error boundaries everywhere (useful for 3rd party component libraries and similar).

Given onCaughtError, onUncaughtError and componentDidCatch all have identical APIs

declare function componentDidCatch(error: any, info: React.ErrorInfo): void;

This PR introduces Sentry.reactErrorHandler, which looks like so:

import * as Sentry from '@sentry/react';
import { hydrateRoot } from "react-dom/client";

ReactDOM.hydrateRoot(
  document.getElementById("root") as HTMLElement,
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  {
    onUncaughtError: Sentry.reactErrorHandler(),
    onCaughtError: Sentry.reactErrorHandler((error, errorInfo) => {
      // optional callback if users want custom config.
    }),
  }
);

To validate this change, we add a react 19 e2e test.

Next Steps

After we merge this in and update the docs, we can explore automatically instrumenting createRoot from react-dom to add Sentry.reactErrorHandler accordingly, but that is a next step.

AbhiPrasad avatar May 21 '24 15:05 AbhiPrasad

size-limit report 📦

Path Size
@sentry/browser 21.78 KB (0%)
@sentry/browser (incl. Tracing) 32.79 KB (-0.01% 🔽)
@sentry/browser (incl. Tracing, Replay) 68.26 KB (0%)
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 61.68 KB (+0.01% 🔺)
@sentry/browser (incl. Tracing, Replay with Canvas) 72.31 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback) 84.37 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback, metrics) 85.84 KB (0%)
@sentry/browser (incl. metrics) 23.17 KB (0%)
@sentry/browser (incl. Feedback) 37.8 KB (0%)
@sentry/browser (incl. sendFeedback) 26.36 KB (0%)
@sentry/browser (incl. FeedbackAsync) 30.79 KB (0%)
@sentry/react 24.5 KB (+0.11% 🔺)
@sentry/react (incl. Tracing) 35.81 KB (+0.08% 🔺)
@sentry/vue 25.73 KB (0%)
@sentry/vue (incl. Tracing) 34.59 KB (0%)
@sentry/svelte 21.92 KB (0%)
CDN Bundle 23.01 KB (0%)
CDN Bundle (incl. Tracing) 34.27 KB (+0.01% 🔺)
CDN Bundle (incl. Tracing, Replay) 68.08 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback) 73.09 KB (0%)
CDN Bundle - uncompressed 67.88 KB (0%)
CDN Bundle (incl. Tracing) - uncompressed 101.68 KB (0%)
CDN Bundle (incl. Tracing, Replay) - uncompressed 211.58 KB (0%)
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 223.96 KB (0%)
@sentry/nextjs (client) 35.14 KB (0%)
@sentry/sveltekit (client) 33.39 KB (0%)
@sentry/node 114.64 KB (-0.01% 🔽)
@sentry/aws-serverless 103.32 KB (+0.01% 🔺)

github-actions[bot] avatar May 21 '24 16:05 github-actions[bot]