Mastering Error Boundaries in React and Next.js: Prevent Crashes and Improve UX
This guide explains how React Error Boundaries work, shows step‑by‑step implementations for Next.js, and demonstrates handling synchronous, asynchronous, and server‑side errors to keep applications stable and provide a better user experience.
Error Boundaries are a React feature introduced in version 16 that lets developers catch rendering errors in a component tree and display a fallback UI instead of crashing the entire application.
<code>class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
console.log(error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
function MyComponent() {
throw new Error('This is an error!');
return <div>My Component</div>;
}
</code>Before React 16, an error in any component would unmount the whole tree. With Error Boundaries, errors are isolated and a fallback UI can be shown, improving robustness especially in large applications.
Error Boundaries
Example: a component fetching user data may fail due to network issues; without an error boundary the whole app crashes, but with one you can render a friendly error message.
The two lifecycle methods used are:
static getDerivedStateFromError(error): called when a child throws, allowing state update to render fallback UI.
componentDidCatch(error, info): used to log error details.
Next.js does not provide a built‑in error boundary, so you need to create your own.
How to Use Error Boundaries in Next.js
First create an ErrorBoundary component:
<code>// components/ErrorBoundary.js
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error("Error occurred:", error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>出了点问题,请稍后再试。</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;
</code>Then wrap pages with the boundary:
<code>// pages/index.js
import ErrorBoundary from '../components/ErrorBoundary';
import FaultyComponent from '../components/FaultyComponent';
export default function Home() {
return (
<div>
<h1>欢迎使用Next.js错误边界示例</h1>
<ErrorBoundary>
<FaultyComponent />
</ErrorBoundary>
</div>
);
}
</code>FaultyComponent simulates an error:
<code>// components/FaultyComponent.js
import React from 'react';
const FaultyComponent = () => {
// Simulate an error
throw new Error("这是一个模拟的错误!");
return <div>你永远不会看到这段文字。</div>;
};
export default FaultyComponent;
</code>When visiting the page, the app shows the fallback UI instead of crashing.
Improving the Fallback UI
The basic UI can be enhanced with a retry button, error‑logging service, or contact‑support information.
<code>// components/ErrorBoundary.js (enhanced)
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error("错误:", error, errorInfo);
}
handleRetry = () => {
this.setState({ hasError: false });
};
render() {
if (this.state.hasError) {
return (
<div>
<h2>哎呀,出错了。</h2>
<p>请尝试刷新页面或联系支持。</p>
<button onClick={this.handleRetry}>重试</button>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
</code>Handling Asynchronous Errors
Error boundaries do not catch errors in async code; you must handle them manually.
<code>// components/AsyncComponent.js
import React, { useState, useEffect } from 'react';
const AsyncComponent = () => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch('/api/data');
if (!res.ok) {
throw new Error("网络响应不正常");
}
const json = await res.json();
setData(json);
} catch (err) {
setError(err);
}
};
fetchData();
}, []);
if (error) {
return <div>错误:{error.message}</div>;
}
return data ? <div>{data.message}</div> : <div>加载中...</div>;
};
export default AsyncComponent;
</code>This displays a user‑friendly message when a fetch fails.
Server‑Side Error Handling in Next.js
Next.js allows custom server error pages via pages/_error.js :
<code>// pages/_error.js
function Error({ statusCode }) {
return (
<p>
{statusCode
? `服务器发生了错误 ${statusCode}`
: '客户端发生了错误'}
</p>
);
}
Error.getInitialProps = ({ res, err }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
return { statusCode };
};
export default Error;
</code>Combined with client‑side error boundaries, you can handle both client and server errors gracefully.
Conclusion
These are the ways to handle exceptions in Next.js; hope they are useful.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
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.