Mastering React Error Boundaries: Build a Flexible Error‑Handling Wheel
This tutorial walks through why catching front‑end component errors is essential, demonstrates how to implement a custom React ErrorBoundary with flexible fallback options, reset logic, higher‑order component wrappers, and hooks, and provides complete TypeScript examples and best‑practice summaries.
What Happened?
A front‑end developer receives a panic call from the boss: the page went white. After opening the console, the developer sees a response from the back‑end that contains a
retcodeand a
dataarray with a valid
Userobject, an
undefinedentry and a
nullentry. The developer initially filters out falsy values and thinks everything is fine, but later discovers that the back‑end returned an unexpected string "User not found" for some items, causing the UI to crash.
The root cause is the lack of proper type checking and error handling on the front‑end side. The correct way to handle such component exceptions in React is to use an Error Boundary .
Step 1: Copy the Official Example
Copy the example from the React documentation and output an
ErrorBoundarycomponent:
<code>class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render shows the fallback UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
logger.error(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
</code>Wrap the business component with the boundary:
<code><ErrorBoundary>
<UserList />
</ErrorBoundary>
</code>When
UserListthrows,
getDerivedStateFromErrorupdates the state and the fallback UI is displayed instead of the broken component.
Step 2: Build a Flexible Wheel
Instead of hard‑coding the fallback UI, expose three ways to provide a fallback:
fallback– a React element.
FallbackComponent– a component that receives the error as a prop.
fallbackRender– a render function that receives
FallbackProps.
<code>type FallbackElement = React.ReactElement | null;
interface FallbackProps { error: Error; }
interface ErrorBoundaryProps {
fallback?: FallbackElement;
FallbackComponent?: React.ComponentType<FallbackProps>;
fallbackRender?: (props: FallbackProps) => FallbackElement;
onError?: (error: Error, info: string) => void;
}
class ErrorBoundary extends React.Component<React.PropsWithChildren<ErrorBoundaryProps>, { error: Error | null }> {
static getDerivedStateFromError(error) { return { error }; }
componentDidCatch(error, errorInfo) { this.props.onError?.(error, errorInfo.componentStack); }
render() {
const { fallback, FallbackComponent, fallbackRender, children } = this.props;
const { error } = this.state;
if (error) {
if (React.isValidElement(fallback)) return fallback;
if (fallbackRender) return fallbackRender({ error });
if (FallbackComponent) return <FallbackComponent error={error} />;
throw new Error('ErrorBoundary requires one of fallback, fallbackRender, or FallbackComponent');
}
return children;
}
}
</code>Example usage with the three props:
<code>// Using fallback element
<ErrorBoundary fallback=<div>Something went wrong</div>><UserList /></ErrorBoundary>
// Using a component
<ErrorBoundary FallbackComponent={ErrorFallback}><UserList /></ErrorBoundary>
// Using a render function
<ErrorBoundary fallbackRender={(props) => <ErrorFallback {...props} />}><UserList /></ErrorBoundary>
</code>Step 3: Add Reset Callback
Sometimes the error is temporary (e.g., a 502 response). Provide a
onResetprop and expose
resetErrorBoundaryto the fallback so the user can retry without a full page reload.
<code>const renderFallback = (props) => (
<div>
Something went wrong, you can <button onClick={props.resetErrorBoundary}>reset</button>.
</div>
);
<ErrorBoundary fallbackRender={renderFallback} onReset={() => console.log('reset')} onError={logger.error}>
<UserList />
</ErrorBoundary>
</code>Step 4: Listen to Render for Automatic Reset
Introduce a
resetKeysarray prop. When any value in the array changes, the boundary automatically resets. Also provide
onResetKeysChangefor custom change detection.
<code>function changedArray(a = [], b = []) {
return a.length !== b.length || a.some((item, i) => !Object.is(item, b[i]));
}
class ErrorBoundary extends React.Component {
componentDidUpdate(prevProps) {
if (changedArray(prevProps.resetKeys, this.props.resetKeys)) {
this.props.onResetKeysChange?.(prevProps.resetKeys, this.props.resetKeys);
this.reset();
}
}
// reset() clears the error state
}
</code>To avoid resetting while the component is still rendering the error UI, a flag
updatedWithErroris used to differentiate the first render caused by the error from subsequent renders.
Step 5: Export a Higher‑Order Component
Wrap any component with
withErrorBoundaryto avoid repetitive JSX:
<code>function withErrorBoundary(Component, errorBoundaryProps) {
const Wrapped = (props) => (
<ErrorBoundary {...errorBoundaryProps}>
<Component {...props} />
</ErrorBoundary>
);
const name = Component.displayName || Component.name || 'Component';
Wrapped.displayName = `withErrorBoundary(${name})`;
return Wrapped;
}
const UserWithErrorBoundary = withErrorBoundary(User, { onError: () => logger.error('error'), onReset: () => console.log('reset') });
</code>Step 6: Provide a Hook for Manual Error Throwing
The
useErrorHandlerhook lets developers throw errors from inside hooks or event handlers so that an outer
ErrorBoundarycan catch them.
<code>function useErrorHandler(givenError) {
const [error, setError] = React.useState(null);
if (givenError) throw givenError;
if (error) throw error;
return setError;
}
function Greeting() {
const [greeting, setGreeting] = React.useState(null);
const handleError = useErrorHandler();
const handleSubmit = (e) => {
e.preventDefault();
const name = e.target.elements.name.value;
fetchGreeting(name).then(setGreeting, handleError);
};
return greeting ? <div>{greeting}</div> : (
<form onSubmit={handleSubmit}>
<label>Name</label>
<input id="name" />
<button type="submit">Get greeting</button>
</form>
);
}
export default withErrorBoundary(Greeting);
</code>Final Summary
Implemented a reusable
ErrorBoundarycomponent.
Used
componentDidCatchand
getDerivedStateFromErrorto capture errors and store the
Errorobject.
Provided three ways to render fallback UI:
fallback,
FallbackComponent, and
fallbackRender.
Added reset capabilities via
onReset,
resetErrorBoundary, and the
resetKeysprop.
Implemented automatic reset when
resetKeyschanges, with optional
onResetKeysChangefor custom logic.
Offered a higher‑order component
withErrorBoundaryand a hook
useErrorHandlerfor flexible integration.
All code is available on GitHub: https://github.com/Haixiang6123/learn-error-bounday Reference library: https://www.npmjs.com/package/react-error-boundary
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.