React 16 introduces a new concept called Error Boundaries which aims to provide proper error handling in the event of an error in a part of the App’s UI.
You might have seen something like this:
Ideally, when an error occurs during rendering, we would want to catch it and display a fallback UI instead of the regular error page. And Error Boundaries can help achieve it.
Defining an Error Boundary
As of this writing, an Error Boundary can only be created using class components.
In order for a component to become an Error Boundary, it must contain either (or both) of the following lifecycle methods:
The former allows us to render a fallback UI while the latter allows us to log the error information (the info can then be sent to a third-party service like Sentry).
class ErrorBoundary() {
state = {
hasError: false,
}
static getDerivedStateFromError(error) {
//set hasError to true if there's an error
return {
hasError: true,
}
}
componentDidCatch(error, info) {
// Send the componentStack info to an external logging service
sendErrorToService(info.componentStack);
}
render() {
if (this.state.hasError) {
// Display a fallbackUI in the event of an error
return <p>This is a human-friendly error message - Oops, something went wrong!</p>
}
// Otherwise, display default UI
return this.props.children;
}
}
And to use it, simply wrap it around top level components like so:
<ErrorBoundary>
<Navigation />
<SideBar />
</ErrorBoundary>
If an error is thrown in either the <Navigation />
or <Sidebar/>
component, the fallback UI will be displayed. However, in certain cases, this might not be ideal because if the error happens within a component, other components shouldn’t be affected.
To prevent that from happening, we can choose to be more granular and wrap the Error Boundary around individual components instead:
<ErrorBoundary>
<Navigation />
</ErrorBoundary>
<ErrorBoundary>
<SideBar />
</ErrorBoundary>
Use case
Let’s look at a simple example of when Error Boundaries can be useful. Using the <ErrorBoundary />
component above, imagine we have a Friend list application that takes in an array of objects, converts the name property to uppercase (for the fun of it!) and displays them.
import React from 'react'
const friends = [
{
name: 'Edmund',
online: true,
},
{
name: 'Jill',
online: false
},
{
name: "Mary",
online: true,
},
{
// Intentionally left out the name property to mimic unpredictable response from Server
online: false,
}
];
// FriendRow component
function FriendRow({friend}) {
return <p>{friend.name.toUpperCase()}</p>
}
// FriendList component
function FriendList() {
const list = friends.map(friend => {
return (
<ErrorBoundary>
<FriendRow friend={friend} />
</ErrorBoundary>
)
})
return (
<div>
<h1>Friend list</h1>
{list}
</div>
)
}
// App component
function App() {
return(
<div id="App">
<FriendList />
</div>
)
}
Let’s assume the friends array is some JSON data we receive from a server (which we do not have control of) and that the data model isn’t always what we expect (hence the missing “name” property in the last object). Due to the missing name property, the application will throw the following error:
But because we wrap our <FriendRow />
component in an Error Boundary, a human-friendly error message is displayed instead:
Also, since our Error Boundary is wrapping individual <FriendRow />
, the error doesn’t cause the other valid components to break!
Conclusion
Error Boundaries, just like regular try/catch is useful for handling unpredictable error exceptions. Instead of a scary wall of red messages, we can now display something friendly/cool and improve our application’s overall UX.
Don’t forget to handle errors gracefully!
Leave a Reply