Client-side data fetching is a crucial aspect of modern web development, especially when building dynamic and interactive user interfaces. In this section, we will explore how to implement client-side data fetching in Next.js, a popular React framework for building server-rendered and statically generated web applications.
Client-side data fetching refers to the process of retrieving data from an external API or database after the initial page load. This approach is beneficial when you need to fetch data that changes frequently or is specific to user interactions, such as search results or personalized content.
In Next.js, client-side data fetching can be achieved using React's built-in useEffect hook in combination with asynchronous functions like fetch. This method allows you to fetch data dynamically and update the UI accordingly.
Before we dive into client-side data fetching, ensure you have a Next.js project set up. If you haven't already, you can create a new Next.js app using the following command:
npx create-next-app@latest my-nextjs-app
cd my-nextjs-app
This will set up a basic Next.js application with all necessary dependencies.
Let's start by fetching data from an external API and displaying it on a page. We'll use the useEffect hook to perform the fetch operation when the component mounts.
Create a new file named fetch-data.js inside the pages directory:
// pages/fetch-data.js
import { useState, useEffect } from 'react';
export default function FetchData() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
}
fetchData();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Data Fetched</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
useState hook to manage the state of our data, loading status, and any potential errors.useEffect hook is used to perform side effects in function components. Here, we fetch data when the component mounts ([] as the second argument).fetch API to make HTTP requests. It returns a promise that resolves to the response of the request.Start your Next.js development server:
npm run dev
Navigate to http://localhost:3000/fetch-data in your browser. You should see the data fetched from the API displayed on the page.
Sometimes, you might want to fetch data conditionally based on certain conditions. For example, fetching user-specific data only if a user is logged in.
// pages/user-data.js
import { useState, useEffect } from 'react';
export default function UserData({ isLoggedIn }) {
const [userData, setUserData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!isLoggedIn) return;
async function fetchUserData() {
try {
const response = await fetch('https://api.example.com/user-data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setUserData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
}
fetchUserData();
}, [isLoggedIn]);
if (!isLoggedIn) return <p>Please log in to see your data.</p>;
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>User Data</h1>
<pre>{JSON.stringify(userData, null, 2)}</pre>
</div>
);
}
You can fetch multiple resources simultaneously using Promise.all to improve performance.
// pages/multiple-data.js
import { useState, useEffect } from 'react';
export default function MultipleData() {
const [data1, setData1] = useState(null);
const [data2, setData2] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const [response1, response2] = await Promise.all([
fetch('https://api.example.com/data1'),
fetch('https://api.example.com/data2')
]);
if (!response1.ok || !response2.ok) {
throw new Error('Network response was not ok');
}
const result1 = await response1.json();
const result2 = await response2.json();
setData1(result1);
setData2(result2);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
}
fetchData();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Data from Multiple Sources</h1>
<pre>{JSON.stringify(data1, null, 2)}</pre>
<pre>{JSON.stringify(data2, null, 2)}</pre>
</div>
);
}
Promise.all to fetch multiple resources simultaneously.Client-side data fetching is a powerful feature in Next.js that allows you to build dynamic and interactive web applications. By using React's hooks and the native fetch API, you can efficiently manage data retrieval and update your UI accordingly. Remember to follow best practices for error handling, performance optimization, and security to ensure a robust application.
In the next section, we will explore server-side data fetching in Next.js, which offers different advantages depending on your use case.