Context
We're developing a React-based app that is being used as a 'widget' on other websites. Our entire react app is wrapped in an iframe using react-frame-component.
For developing purposes, we use Sentry to log any errors (@sentry/browser sdk).
Problem
We noticed that once our app is integrated on any website errors don't get logged to Sentry anymore and we're not entirely sure how to solve this.
So we're seeking a solution to log errors to Sentry that occur inside an iframe and ONLY inside that iframe.
Is it possible to somehow tell Sentry what window/target/scope to use?
Using React Error Boundaries might be an option to consider. However, it doesn't capture all errors.
Iframe - code
...
render(
<Iframe initialContent={initialIframeContent}>
<App />
</Iframe>,
document.getElementById(containerId)
);
...
Sentry integration - code
...
Sentry.init({
environment: env,
dsn: SENTRY_DSN
});
...
We couldn't find a direct solution so we ended up using React Error Boundaries to capture any crash and log it to sentry.
Keep in mind that Error Boundaries doesn't capture all crashes (see quote below). So we came up with a hook that would make Error Boundaries capture those crashes as well (except for the server side rendering and crashes inside Error Boundaries itself).
See inspiration hook.
Note
Error boundaries do not catch errors for:
- Event handlers (learn more)
- Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
- Server side rendering
- Errors thrown in the error boundary itself (rather than its children)
~ React
ErrorBoundary.js
const initialState = {
hasError: false,
showError: false
};
export default class ErrorBoundary extends Component {
static propTypes = {
children: PropTypes.node.isRequired
};
static getDerivedStateFromError () {
return { hasError: true };
}
state = initialState;
componentDidCatch (error) {
logToSentry(error);
};
...
See sentry docs
useError.js
const useError = () => {
const [_, setError] = useState();
return useCallback(
e => {
setError(() => {
throw e;
});
},
[setError],
);
};
export default useError;
Usage sample
const throwError = useError();
const someAsyncFn = useCallback(async () => {
try {
const response = await asyncApiRequest();
} catch (e) {
throwError(e)
}
}, []);
const someEventHandler = useCallback(async () => {
try {
doSomethingThatMightCrash();
} catch (e) {
throwError(e)
}
}, []);
It's a bit tedious and there might be ways to simplify this even more but it does its job.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With