Chapter 1 of 4
Why Boundaries Exist
What happens without one, and the two lifecycle methods that make one.
Since React 16, an error thrown during rendering unmounts the entire component tree. The reasoning is that a half-rendered interface is worse than none: a banking app showing the wrong balance is more dangerous than one showing an error.
An error boundary catches errors below it and renders a fallback instead, so the failure is contained to one part of the screen.
Boundaries must be class components
There is still no hook equivalent. A boundary implements one or both of two lifecycle methods.
class ErrorBoundary extends React.Component {
state = { error: null };
// Render phase: return the new state, must be pure
static getDerivedStateFromError(error) {
return { error };
}
// Commit phase: side effects such as logging belong here
componentDidCatch(error, errorInfo) {
logToService(error, errorInfo.componentStack);
}
render() {
if (this.state.error) {
return this.props.fallback ?? <p>Something went wrong.</p>;
}
return this.props.children;
}
}The division of labour
getDerivedStateFromErrorruns during rendering, so it must be pure - just return the state that shows the fallback.componentDidCatchruns after the commit, so logging, analytics and anything with a side effect goes there. It also receives the component stack.
import { ErrorBoundary } from "react-error-boundary";
<ErrorBoundary
FallbackComponent={ErrorFallback}
onReset={() => refetch()}
>
<Dashboard />
</ErrorBoundary>