Meistern von Data Fetching in Next.js
Min-jun Kim
Dev Intern · Leapcell

Einleitung
In der sich ständig weiterentwickelnden Landschaft der Frontend-Entwicklung ist effizientes Data Fetching entscheidend für den Aufbau performanter und benutzerfreundlicher Anwendungen. Next.js, ein beliebtes React-Framework, hat in diesem Bereich kontinuierlich innoviert und bietet leistungsstarke Strategien zum Abrufen und Rendern von Daten. Mit dem Aufkommen von React Server Components und ihrer Integration in Next.js hat sich das Paradigma für Data Fetching erheblich verschoben. Das Verständnis dieser neuen Ansätze – insbesondere der Unterschiede und des Zusammenspiels zwischen Client Components, Server Components und der erweiterten fetch
-API – ist für Entwickler, die moderne, hocheffiziente Webanwendungen erstellen möchten, von entscheidender Bedeutung. Dieser Artikel befasst sich mit diesen wesentlichen Konzepten und bietet praktische Anleitungen und Codebeispiele, die Ihnen helfen, Data Fetching in Next.js zu meistern und außergewöhnliche Benutzererlebnisse zu liefern.
Kernkonzepte und Strategien
Bevor wir uns mit den Implementierungsdetails befassen, sollten wir ein klares Verständnis der Kernkonzepte entwickeln, die den Data-Fetching-Strategien von Next.js zugrunde liegen.
Client Components und Server Components
Client Components (CCs): Dies sind die bekannten React-Komponenten, die vollständig auf der Client-Seite im Browser des Benutzers ausgeführt und neu gerendert werden. Sie können React Hooks wie useState
und useEffect
für Interaktivität und Seiteneffekte nutzen. Für das Data Fetching verwenden Client Components typischerweise useEffect
in Verbindung mit Bibliotheken wie axios
, swr
oder react-query
oder die native fetch
-API.
Server Components (SCs): Eine bahnbrechende Innovation, Server Components werden ausschließlich auf dem Server ausgeführt. Sie rendern einmal während der Anfrage (oder zur Build-Zeit) und senden das resultierende HTML und die notwendigen Bundles an den Client. Da sie auf dem Server ausgeführt werden, können Server Components direkt auf serverseitige Ressourcen wie Datenbanken, Dateisysteme oder private APIs zugreifen, ohne sensible Informationen an den Client preiszugeben. Sie haben keinen Zustand oder keine Effekte und können Benutzerinteraktionen nicht direkt verarbeiten. Ihre Hauptanwendungsfälle umfassen das Abrufen von Daten, den Zugriff auf serverseitige Logik und das Rendern statischer oder dynamischer Inhalte, die keine clientseitige Interaktivität erfordern.
Die erweiterte fetch
-API
Next.js erweitert die native fetch
-API um leistungsfähige Funktionen, insbesondere wenn sie innerhalb von Server Components verwendet wird. Diese Erweiterungen umfassen die automatische Anforderungsmemoisation, die Revalidierung durch next / cache
und die Unterstützung für async / await
direkt in den Komponentendefinitionen. Diese Integration bietet eine optimierte Möglichkeit, Daten mit integrierter Caching- und Revalidierungslogik abzurufen.
Data Fetching in Client Components
Für interaktive Teile Ihrer Anwendung oder wenn Sie Echtzeitaktualisierungen basierend auf Benutzeraktionen benötigen, sind Client Components die richtige Wahl. Oft ruft man Daten nach dem ersten Rendern mithilfe von useEffect
ab.
// components/ClientDataFetcher.jsx 'use client'; // Diese Direktive kennzeichnet sie als Client Component import { useState, useEffect } from 'react'; export default function ClientDataFetcher() { 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/public-data'); // Ruft von einem öffentlichen API-Endpunkt ab if (!response.ok) { throw new Error('Failed to fetch data'); } const json = await response.json(); setData(json); } catch (err) { setError(err); } finally { setLoading(false); } } fetchData(); }, []); // Leeres Abhängigkeitsarray stellt sicher, dass es einmal beim Mounten ausgeführt wird if (loading) return <p>Loading client data...</p>; if (error) return <p>Error: {error.message}</p>; return ( <div> <h2>Client-Fetched Data</h2> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); }
In diesem Beispiel kennzeichnet die Direktive 'use client'
ClientDataFetcher
eindeutig als Client-Komponente. Daten werden nach dem Mounten der Komponente mithilfe von useEffect
abgerufen, was Interaktivität und die Fähigkeit zur Aktualisierung von Daten basierend auf clientseitigen Ereignissen gewährleistet.
Data Fetching in Server Components
Server Components eignen sich hervorragend zum direkten Abrufen von Daten auf dem Server, bevor die Komponente an den Client gesendet wird. Dies bietet mehrere Vorteile, darunter verbesserte Leistung (kein zusätzlicher Netzwerkhin und zurück vom Client für Daten), erhöhte Sicherheit (keine preisgegebenen API-Schlüssel) und vereinfachter Code.
// app/page.jsx (Dies ist standardmäßig eine Server Component im App Router) // Für RSCs können Sie die Komponente direkt asynchron machen async function getServerData() { // Verwendung einer internen API-Route als Beispiel oder direkte Verbindung zu einer Datenbank const response = await fetch(`${process.env.API_BASE_URL}/api/products`, { cache: 'no-store', // Opt-out des Cachings für diese Anfrage // next: { revalidate: 60 } // Revalidieren Sie diese Daten alle 60 Sekunden }); if (!response.ok) { throw new Error('Failed to fetch server data'); } return response.json(); } export default async function HomePage() { const products = await getServerData(); // Daten werden auf dem Server abgerufen return ( <div> <h1>Willkommen im Next.js Store!</h1> <h2>Unsere Produkte (Server-Fetched)</h2> <ul> {products.map(product => ( <li key={product.id}> {product.name} - ${product.price} </li> ))} </ul> {/* Client Components können innerhalb von Server Components gerendert werden */} {/* Zum Beispiel wäre ein "In den Warenkorb"-Button eine Client Component */} {/* <AddToCartButton productId={product.id} /> */} </div> ); }
Hier ist HomePage
eine Server Component (impliziert durch ihre Position im app
-Verzeichnis und das Fehlen der Direktive 'use client'
). Die Funktion getServerData
wird direkt in der Komponente aufgerufen und auf ihr Ergebnis gewartet, bevor gerendert wird. Die fetch
-Optionen wie cache: 'no-store'
oder next: { revalidate: 60 }
(für zeitbasierte Revalidierung) demonstrieren die erweiterten Fähigkeiten von fetch
innerhalb von Next.js. cache: 'no-store'
bedeutet, dass Daten bei jeder Anfrage abgerufen werden, während revalidate
die Hintergrundrevalidierung ermöglicht.
Kombination von Client- und Server Components
Die Stärke von Next.js liegt in seiner Fähigkeit, Client- und Server Components nahtlos zu kombinieren. Server Components können Client Components rendern und Daten als Props weitergeben. Dies ermöglicht es Ihnen, statische oder häufig abgerufene Daten auf dem Server abzurufen und gleichzeitig reichhaltige Interaktivität dort bereitzustellen, wo sie benötigt wird.
// app/product/[id]/page.jsx (Server Component für dynamisches Routing) import AddToCartButton from '@/components/AddToCartButton'; // Dies ist eine Client Component async function getProductDetails(id) { const response = await fetch(`${process.env.API_BASE_URL}/api/products/${id}`); if (!response.ok) { throw new Error(`Failed to fetch product ${id}`); } return response.json(); } export default async function ProductDetailsPage({ params }) { const product = await getProductDetails(params.id); return ( <div> <h1>{product.name}</h1> <p>{product.description}</p> <p>Price: ${product.price}</p> {/* Übergabe von server-abgerufenen Daten als Props an eine Client Component */} <AddToCartButton productId={product.id} productName={product.name} /> </div> ); } // components/AddToCartButton.jsx 'use client'; import { useState } from 'react'; export default function AddToCartButton({ productId, productName }) { const [isLoading, setIsLoading] = useState(false); const [message, setMessage] = useState(''); const handleAddToCart = async () => { setIsLoading(true); setMessage(''); try { // Simulieren des API-Aufrufs zum Hinzufügen zum Warenkorb await new Promise(resolve => setTimeout(resolve, 1000)); // In einer realen App würden Sie hier eine fetch POST-Anfrage stellen setMessage(`${productName} added to cart!`); } catch (error) { setMessage('Failed to add to cart.'); } finally { setIsLoading(false); } }; return ( <button onClick={handleAddToCart} disabled={isLoading}> {isLoading ? 'Adding...' : 'Add to Cart'} {message && <p>{message}</p>} </button> ); }
In diesem Setup ruft ProductDetailsPage
(eine Server Component) Produktdaten ab. Anschließend rendert sie AddToCartButton
(eine Client Component) und übergibt productId
und productName
als Props. Der Button selbst kümmert sich um clientseitigen Zustand und Interaktion und löst bei Klick einen API-Aufruf aus. Dieses Muster nutzt die Stärken beider Komponententypen.
Fazit
Next.js bietet eine leistungsstarke und flexible Auswahl an Data-Fetching-Strategien, die Entwicklern die Werkzeuge an die Hand geben, um hocheffiziente und robuste Webanwendungen zu erstellen. Indem Sie die Kernunterschiede zwischen Client Components und Server Components verstehen und die erweiterte fetch
-API nutzen, können Sie fundierte Entscheidungen darüber treffen, wo und wie Sie Ihre Daten abrufen. Dieser Ansatz führt zu verbesserter Leistung, besserer Sicherheit und einem überlegenen Benutzererlebnis, was Next.js zu einer ausgezeichneten Wahl für die moderne Webentwicklung macht.