React JS provides errorBoundary
to handle the unexpected errors by providing fallback UI.
Think of Error boundary is catch as we have in JavaScript.
You can watch my video on error boundary at my YouTube channel - Here
Problem Statement
In our ReactJS application, when we have components which are 3rd party dependent or have API calls or DB data. There could be unexpected situation where because of the some issue from 3rd party or things beyond our control, our application might face error. As a result, our application will crash and users will be left with a white screen.
We should be avoiding white screen. We should provide the user with an error message or next steps.
Error boundary helps us in replacing white screen with meaning full , and custom error messages.
Solution
In ReactJS native, there is ErrorBoundary. This is class based component and has limited flexibility.
One can use 'react-error-boundary' package. The reasons to go with react-error-boundary
are:
Can work with functional component
Provide more flexibility
Can log the errors and integrate with 3rd party eg: sentry
Can provide reset app functionality.
code time
Please check the code here.
Install npm i react-error-boundary
1 . *Inline Fallback *
// App.jsx
import React, { useState, useEffect } from "react";
import { ErrorBoundary } from "react-error-boundary";
import CardList from "./CardList";
function App() {
const [characters, setCharacters] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("https://rickandmortyapi.com/api/character")
.then((response) => response.json())
.then((data) => {
setCharacters(data.results);
setLoading(false);
})
.catch((error) => console.error("Error fetching data:", error));
}, []);
return (
<div className="bg-gray-100 min-h-screen p-8">
<h1 className="text-4xl font-bold text-center mb-8 text-gray-800">
Rick and Morty Characters: Error Boundary Example
</h1>
{loading ? (
<p className="text-center text-gray-500">Loading...</p>
) : (
<ErrorBoundary fallback={<h1>Error</h1>}>
<CardList characters={characters} />
</ErrorBoundary>
)}
</div>
);
}
export default App;
2 . Fallback component
Create a component with your desire message, and UI.
// App.jsx
import React, { useState, useEffect } from "react";
import { ErrorBoundary } from "react-error-boundary";
import CardList from "./CardList";
import ErrorFallback from "./ErrorFallback";
function App() {
const [characters, setCharacters] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("https://rickandmortyapi.com/api/character")
.then((response) => response.json())
.then((data) => {
setCharacters(data.results);
setLoading(false);
})
.catch((error) => console.error("Error fetching data:", error));
}, []);
return (
<div className="bg-gray-100 min-h-screen p-8">
<h1 className="text-4xl font-bold text-center mb-8 text-gray-800">Rick and Morty Characters: Error Boundary Example</h1>
{loading ? (
<p className="text-center text-gray-500">Loading...</p>
) : (
<ErrorBoundary
FallbackComponent={ErrorFallback}
>
<CardList characters={characters} />
</ErrorBoundary>
)}
</div>
);
}
export default App;
// ErrorFallBack.jsx
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div className="text-center p-8">
<h1 className="text-3xl font-bold text-red-600">Something went wrong!</h1>
</div>
);
}
export default ErrorFallback;
3 . Logging
You can log the error and error information to your console, backend, file, or integration with any 3rd party API or application such as sentry.
//App.jsx
function logErrorToService(error, errorInfo) {
// Here you can send the error to an external logging service
console.error("Logging to error service: ", { error, errorInfo });
}
4 . Reset
In this, we can let user to re-try by clicking on button. In our example, we are doing an API request. So, we can re- request the API request on click of Button.
// App.jsx
import React, { useState, useEffect } from "react";
import { ErrorBoundary } from "react-error-boundary";
import CardList from "./CardList";
import ErrorFallback from "./ErrorFallback";
function App() {
const [characters, setCharacters] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("https://rickandmortyapi.com/api/character")
.then((response) => response.json())
.then((data) => {
setCharacters(data.results);
setLoading(false);
})
.catch((error) => console.error("Error fetching data:", error));
}, []);
function logErrorToService(error, errorInfo) {
// Here you can send the error to an external logging service
console.error("Logging to error service: ", { error, errorInfo });
}
return (
<div className="bg-gray-100 min-h-screen p-8">
<h1 className="text-4xl font-bold text-center mb-8 text-gray-800">Rick and Morty Characters: Error Boundary Example</h1>
{loading ? (
<p className="text-center text-gray-500">Loading...</p>
) : (
<ErrorBoundary
FallbackComponent={ErrorFallback}
onError={(error, errorInfo) => logErrorToService(error, errorInfo)}
onReset={() => {
setLoading(true);
fetch("https://rickandmortyapi.com/api/character")
.then((response) => response.json())
.then((data) => {
setCharacters(data.results);
setLoading(false);
});
}}
>
<CardList characters={characters} />
</ErrorBoundary>
)}
</div>
);
}
export default App;
// ErrorComponentFallback.jsx
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div className="text-center p-8">
<h1 className="text-3xl font-bold text-red-600">Something went wrong!</h1>
<p className="text-gray-500 mt-4">Error: {error.message}</p>
<button
onClick={resetErrorBoundary}
className="mt-4 bg-blue-500 text-white p-2 rounded"
>
Try Again
</button>
</div>
);
}
export default ErrorFallback;
Where to use Error Boundary
You will be tempted to wrap every component with error boundary. However, there is little cost of the performance you have to bear. So, prefer to identify the components which could be "error sensitive". A few places where you should be using Error Boundary:
Components with 3rd party api and data
In most real-world applications, error boundaries are only placed around critical parts of the application (e.g., around major components or entire routes), so the performance impact is minimal.
Prefer to wrap the parent component with Error boundary.
Where not to use error boundary
Error boundaries only catch rendering errors, lifecycle method errors, and errors in constructors, not in event handlers.
Do not use errorBoundary in event handlers. You can use try-catch
.
A note on performance
Whenn an error is caught, React needs to re-render the components in the tree that are within the error boundary’s scope, which can introduce a slight delay as it tries to render the fallback UI.
If you use too many error boundaries throughout the application (at a very granular level), this can lead to unnecessary re-renders and increased component complexity, slightly degrading performance.
But it is always about trade-off, stability of app vs performance. If the performance cost is not much always go for the stability of the app or try to balance both.
Like it? Spread the knowledge and do say hi to me at twitter
Happy Learning
Top comments (0)