
Handling Data Loading and Error States
As developers, we know that user experience is paramount. In web applications, this often means providing clear feedback to users about what's happening, especially when fetching data. In Next.js, handling loading and error states for your data fetches is straightforward and crucial for a smooth user journey. This section will guide you through common patterns and best practices.
When data is being fetched, it's good practice to show a loading indicator. This lets the user know that the application is working to retrieve the information they requested and prevents them from thinking the page is broken. In Next.js, especially with client-side fetching, you can leverage component state to manage this.
import { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch('/api/mydata');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
}
fetchData();
}, []);
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
return (
<div>
<h1>My Data</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export default MyComponent;The useEffect hook is our workhorse here. We initialize loading to true. Inside the useEffect, we set up an asynchronous function fetchData. We wrap the fetch logic in a try...catch...finally block. The try block attempts to fetch the data and update the data state. If an error occurs during the fetch, the catch block updates the error state. Regardless of success or failure, the finally block ensures setLoading(false) is always called, signaling the end of the loading period.
In the rendering part of our component, we first check the loading state. If it's true, we display a 'Loading...' message. If loading is false and there's an error, we display an error message. Only if both loading and error are false do we render the actual data.
For server-side rendering (SSR) or static site generation (SSG) with getStaticProps or getServerSideProps, Next.js provides a slightly different mechanism. These functions run on the server (or at build time) and can return props to your page component. If an error occurs within these functions, Next.js will typically show an error page by default. However, you can handle errors within these functions and decide what to return.
export async function getServerSideProps(context) {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
// Handle non-OK responses
return {
props: { data: null, error: `Failed to fetch data: ${response.status}` }
};
}
const data = await response.json();
return { props: { data, error: null } };
} catch (error) {
// Handle network or other errors
return { props: { data: null, error: error.message } };
}
}
function MyPage({ data, error }) {
if (error) {
return <div>Error loading data: {error}</div>;
}
return (
<div>
<h1>Server-Rendered Data</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}In this SSR example, getServerSideProps attempts to fetch data. If the response is not ok or if a catch block catches an error, we return props that include an error message. The page component then checks for this error prop and displays a user-friendly message if it exists. This approach ensures that even server-side fetches provide feedback.