I can make a class an error boundary in React by implementing componentDidCatch.
Is there a clean approach to making a functional component into an error boundary without converting it into a class?
Or is this a code smell?
I can make a class an error boundary in React by implementing componentDidCatch.
Is there a clean approach to making a functional component into an error boundary without converting it into a class?
Or is this a code smell?
As of v19.2.0, there's no way to turn a functional component into an error boundary.
The React docs are clear about that, although you're free to reuse them as many times as you wish:
To implement an Error Boundary component, you need to provide static getDerivedStateFromError which lets you update state in response to an error and display an error message to the user. You can also optionally implement componentDidCatch to add some extra logic, for example, to log the error to an analytics service.
There is currently no way to write an Error Boundary as a function component. However, you don’t have to write the Error Boundary class yourself. For example, you can use react-error-boundary instead.
Also bear in mind that try/catch blocks won't work on all cases.
If a component deep in the hierarchy tries to updates and fails, the try/catch block in one of the parents won't work -- because it isn't necessarily updating together with the child.
As mentioned already, the React team has not yet implemented a hook equivalent, and there are no published timelines for a hook implementation.
A few third party packages on npm implement error boundary hooks. I published react-use-error-boundary, attempting to recreate an API similar to useErrorBoundary from Preact:
import { withErrorBoundary, useErrorBoundary } from "react-use-error-boundary";
const App = withErrorBoundary(({ children }) => {
const [error, resetError] = useErrorBoundary(
// You can optionally log the error to an error reporting service
(error, errorInfo) => logErrorToMyService(error, errorInfo)
);
if (error) {
return (
<div>
<p>{error.message}</p>
<button onClick={resetError}>Try again</button>
</div>
);
}
return <div>{children}</div>;
});
There is an implementation that can handle with non-existent functionalities for a functional component such as componentDidCatch and deriveStateFromError.
According to the author, it is based on React.memo().
The proposed solution is greatly inspired by the new React.memo() API.
import Catch from "./functional-error-boundary"
type Props = {
children: React.ReactNode
}
const MyErrorBoundary = Catch(function MyErrorBoundary(props: Props, error?: Error) {
if (error) {
return (
<div className="error-screen">
<h2>An error has occured</h2>
<h4>{error.message}</h4>
</div>
)
} else {
return <React.Fragment>{props.children}</React.Fragment>
}
})
reference and API here
What I have done is create custom class component and wrapped my functional/class component inside it where ever its required. This is how my custom class component looks like:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {error: ""};
}
componentDidCatch(error) {
this.setState({error: `${error.name}: ${error.message}`});
}
render() {
const {error} = this.state;
if (error) {
return (
<div>{error}</div>
);
} else {
return <>{this.props.children}</>;
}
}
}
And used it like this my functional/class components:
<ErrorBoundary key={uniqueKey}>
<FuncationalOrChildComponent {...props} />
</ErrorBoundary>
PS: As usual the key property is very important as it will make sure to re-render the ErrorBoundary component if you have dynamic child elements.
key property is very important as it will make sure to re-render the ErrorBoundary component if you have dynamic child elements. Could you please explain a more on this statement. thanksOfficial React team not provided Error boundary support for functional component. We can achieve error boundary for functional component using npm package. https://www.npmjs.com/package/react-error-boundary
There are some great third party libraries for this but if you are already using Sentry then you can use Sentry.ErrorBoundary
import React from "react";
import * as Sentry from "@sentry/react";
<Sentry.ErrorBoundary fallback={<p>An error has occurred</p>}>
<Example />
</Sentry.ErrorBoundary>;
or as a higher order component
import React from "react";
import * as Sentry from "@sentry/react";
Sentry.withErrorBoundary(Example, { fallback: <p>an error has occurred</p> });
If you are coming to this page in 2023, are using TypeScript and do not want to implement a dependency there is a pretty simple but effective example over on Netlify of how to implement a simple error boundary.
Sadly there is still no functional solution to the problem as per the react docs.
Netlify example: https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/error_boundaries/
import React, { useState, useEffect } from 'react';
import { Button } from 'antd';
const ErrorBoundary = ({ children }) => {
const [hasError, setHasError] = useState(false);
useEffect(() => {
const errorHandler = (event) => {
setHasError(true);
console.error("ErrorBoundary caught an error", event.error);
};
window.addEventListener("error", errorHandler);
window.addEventListener("unhandledrejection", errorHandler);
return () => {
window.removeEventListener("error", errorHandler);
window.removeEventListener("unhandledrejection", errorHandler);
};
}, []);
if (hasError) {
return (
<div style={styles.container}>
<div style={styles.errorBox}>
<h1 style={styles.title}>Oops! Something went wrong.</h1>
<p style={styles.message}>
We're sorry for the inconvenience. Please try again later or contact support if the issue persists.
</p>
<Button type="primary" style={styles.button}>
<a href="/profile">GO BACK</a>
</Button>
</div>
</div>
);
}
return children;
};
const styles = {
container: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
backgroundColor: '#f0f2f5',
},
errorBox: {
textAlign: 'center',
backgroundColor: '#fff',
padding: '40px',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',
},
title: {
fontSize: '24px',
color: '#ff4d4f',
},
message: {
fontSize: '16px',
color: '#595959',
marginBottom: '20px',
},
button: {
marginTop: '20px',
},
};
export default ErrorBoundary;
you can use this functional based ERROR BOUNDARY Component
componentDidCatchis probably considered part of the React lifecycle hooks, which is not available in simple function components. The only way I imagine you could do something similar would be to wrap the entirety of your function body in atry...catchblock...17.01and still, they sayOnly class components can be error boundaries