Zustandsverwaltung in Next.js IV.
Hallo zusammen,
Lassen Sie uns unsere Reise in der Welt des State Managements fortsetzen - es ist das vierte Ziel auf dem Weg - mit dem beliebtesten React-Framework, Next.js. Wenn Sie an neuen Nachrichten interessiert sind und diese nutzen möchten, ist dies genau das Richtige für Sie! Es basiert auf den vorherigen Teilen und erweitert diese, daher wird empfohlen, sie im Voraus zu lesen (erster Teil, zweiter Teil, dritter Teil. Das Jahr 2023 hat bereits viele interessante Dinge in Next.js' und damit in unser Leben gebracht. Wir versuchen nur, mit der wirbelnden Kette von Ereignissen Schritt zu halten. Während ich diese Zeilen schreibe, ist die neueste stabile Version von Next.js 14. Mit der Verwendung des App-Verzeichnisses - das die Wunder von React 18 zum Leben erweckt - müssen wir unseren Code in Bezug auf die Zustandsverwaltung optimieren und ändern. (Wir haben bereits zum Zeitpunkt der Veröffentlichung über den App-Router geschrieben hier.)
Integrierte Lösungen
Werfen wir einen Blick auf die nativen React-State-Management-Lösungen wie useState, useReduce und die React Context API.
Über die Verwendung des Pages-Routers
Falls Ihre Anwendung den Seitenrouter verwendet, bleiben unsere vorherigen Empfehlungen (erster Teil, zweiter Teil, dritter Teil bestehen.
Bevor wir eintauchen - da es unser Thema stark beeinflusst - lassen Sie uns kurz über Server- und Client-Komponenten sprechen.
Informationen zu den neu eingeführten Serverkomponenten und Vorteilen Standardmäßig beginnt jede React-Komponente als Serverkomponente. Hier sind die Vorteile dieses Ansatzes:
- Datenabruf: Da dieser Prozess serverseitig an der Datenquelle (über asynchrone Funktionskomponenten) stattfindet, ist er im Vergleich zu anderen Methoden zum Abrufen von Daten schneller.
- Sicherheit: Sensible Daten wie API-Schlüssel und Token werden serverseitig gespeichert, sodass sie nicht auf die Client-Seite gelangen.
- Zwischenspeicherung: Ergebnisse können auf dem Server zwischengespeichert und für nachfolgende beschleunigte Anforderungen wiederverwendet werden. Dies geschieht automatisch durch und durch Next.js.
- Paketgröße: JS-Module werden importiert und auf dem Server verwendet, sodass der Client JavaScript nicht herunterladen, analysieren und ausführen muss. Dies bietet erhebliche Vorteile bei der SEO-Optimierung, da nutzbare Inhalte früher für die Nutzer erscheinen. Natürlich hat dies einen großen Einfluss auf das anfängliche Laden der Seite, den FCP (First Contentful Paint).
- Streaming: Serverkomponenten ermöglichen es Ihnen, die Rendering-Arbeit in Teile zu zerlegen und sie an den Client zu streamen, sobald sie bereit sind. Auf diese Weise können Benutzer bestimmte Teile der Seite früher sehen, ohne auf das gesamte serverseitige Rendering der Seite warten zu müssen.
Das ist alles sehr schön, sogar erstaunlich, aber wo ist der Haken, könnte man fragen. Die Einschränkung liegt in der Tatsache, dass es bei Serverkomponenten keine Möglichkeit für Benutzerinteraktionen und die Verwendung von Hooks gibt.
Hier kommen die Server-Komponenten ins Bild.
Veränderte Client-Komponenten und ihre Vorteile
Die Komponente, in der wir Interaktivität erwarten, sollte nun die Direktive "use client" am Anfang der Datei enthalten, direkt über den Importen. Mit diesem Schritt wird eine spezielle Grenze festgelegt. Innerhalb der Datei einer Client-Komponente werden andere importierte Module und Komponenten zu Client-Komponenten, so dass unsere vorherigen Komponenten wie gewohnt funktionieren, vor der "App-Router-Ära".
Wie wir auf der linken Seite des Bildes sehen können, handelt es sich bei diesen Komponenten um Serverkomponenten und es wird eine Fehlermeldung angezeigt. Im Gegensatz dazu haben wir auf der rechten Seite des Bildes durch die Verwendung der Direktive "use client" die magische Grenze geschaffen, die für die Verwendung von Hooks erforderlich ist, um Interaktivität zu ermöglichen, ohne auf Fehler zu stoßen.
Das Next.js-Team möchte betonen, dass Clientkomponenten weder besser noch schlechter als Serverkomponenten sind. Sie dienen einfach unterschiedlichen Zwecken. Es ist wichtig, sich daran zu erinnern, dass vor der "App-Router-Ära" jede einzelne Komponente eine Client-Komponente war.
Einfach ausgedrückt: Wenn Sie eine Browser-API oder einen Hook verwenden möchten, lautet die Faustregel, dass Sie "use client" am Anfang Ihrer Komponente einfügen sollten.
- Interaktivität: Clientkomponenten sind in der Lage, Zustandsänderungen, Effekte und Ereignisüberwachung zu verwalten und letztendlich Verbindungen zwischen dem Benutzer und der Benutzeroberfläche herzustellen.
- Browser-API: In Clientkomponenten haben wir Zugriff auf Browser-APIs wie Geolocation und localStorage.
Beispiel für eine Clientkomponente und useState in Aktion
Hier ist ein einfaches Beispiel für eine Server-Komponente, die eine untergeordnete Client-Komponente hat.
page.tsx
import Counter from "@/app/components/Counter"
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-center gap-8">
<h1 className="text-3xl font-bold">My really precious Counter has arrived!</h1>
<Counter/>
</main>
)
}
Counter.tsx
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={(prev) => setCount(prev + 1)}>Click me</button>
</div>
)
}
Verwendete Anbieter
Wie sieht es mit den Anbietern aus?
Die Frage stellt sich schnell. Anbieter sind wesentliche Bestandteile der Zustandsverwaltung, unabhängig davon, ob sie sich auf die Kontext-API oder auf einen Redux-Toolkit-Anbieter beziehen, wobei anerkannt wird, dass ihre Verwendung innerhalb von Serverkomponenten nicht möglich ist.
Aus dem Besprochenen geht hervor, dass sie nur korrekt als Clientkomponenten verwendet werden können. Daher haben wir unseren gesamten Komponentenbaum (unsere gesamte App mit Anbietern) deoptimiert, nur weil wir den Zustand mithilfe von Anbietern behandeln wollten???!? 444
Die Antwort ist NEIN, zum Glück.
Wenn eine Clientkomponente eine Requisite von einer Serverkomponente erhält, z. B. 'children', dann kann es sich immer noch um eine Serverkomponente handeln. Das ist etwas, was wir mit unseren Anbietern nutzen können.
Schauen wir uns ein Beispiel an, wie dies erreicht werden kann.
Beispiel für Provider:
layout.tsx
import {ReactNode} from "react"
import {Inter} from 'next/font/google'
import './globals.css'
import MainProvider from "@/providers/MainProvider"
const inter = Inter({subsets: ['latin']})
export default function RootLayout({
children,
}: {
children: ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>
<MainProvider>
{children}
</MainProvider>
</body>
</html>
)
}
MainProvider.tsx
'use client'
import {ReactNode} from "react"
import {Provider} from 'react-redux'
import {store} from "@/app/store"
const MainProvider = ({children} : {children: ReactNode}) => {
return (
<Provider store={store}>
{children}
</Provider>
)
}
export default MainProvider
Im MainProvider beobachten wir die Verwendung der Direktive 'use client' (die den MainProvider als Clientkomponente bezeichnet). Hier ist es möglich, mehrere Anbieter zu haben, und der *MainProvider umschließt sie, indem er seine untergeordnete Eigenschaft rendert, die, obwohl ihr übergeordnetes Element eine Clientkomponente ist, im Wesentlichen eine Serverkomponente bleibt. Innerhalb des neuen App-Verzeichnis-Router-Ökosystems wird der Inhalt von page.tsx zur untergeordneten Requisite.
page.tsx
import moment from "moment"
import Counter from "@/app/components/Counter"
import Test from "@/app/components/Test"
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-center gap-8">
<h1 className="text-3xl font-bold">My really precious Counter has arrived!</h1>
<p>{moment().format('MMMM Do YYYY, h:mm:ss a')}</p>
<Counter/>
<Test/>
</main>
)
}
In der Datei page.tsx können wir sie als Serverkomponente identifizieren. Wenn das übergeordnete Element, wie layout.tsx, noch nicht "deoptiert" wurde, bleibt es eine Serverkomponente. Nehmen wir an, wir würden in Betracht ziehen, die große JS-Bibliothek 'moment' zu verwenden, um Datumsangaben zu formatieren (obwohl es ratsam ist, dies aufgrund besserer Alternativen wie date-fns oder dem eingebauten Intl) zu vermeiden). Auf diese Weise würden wir den Client nicht belasten, da er ausschließlich serverseitig geladen wird. Folglich wird nur der vollständige HTML-Code an den Browser gesendet, sodass er die Bibliothek nicht herunterladen, analysieren und ausführen muss.
Counter.tsx
'use client'
import {useDispatch, useSelector} from "react-redux"
import {RootState} from "@/app/store"
import {increment, decrement,} from "@/slices/counterSlice"
export default function Counter() {
const count = useSelector((state: RootState) => state.counter.value)
const dispatch = useDispatch()
return (
<>
<p className="text-center">You clicked {count} times</p>
<div className="flex p-2 bg-blue-200 w-96 justify-between">
<button className="p-2 bg-amber-100" onClick={() => dispatch(decrement())}>Decrement</button>
<button className="p-2 bg-amber-100" onClick={() => dispatch(increment())}>Increment</button>
</div>
</>
)
}
Am oberen Rand der Datei "Counter.tsx" beachten wir die Anweisung "Client verwenden", sodass wir die Vorteile der von Redux bereitgestellten Zustandsverwaltung bequem genießen können, indem wir auf die Schaltflächen klicken
Ein paar Worte und Gedanken zum App Router, seiner Stabilität und zu Next.js Version 14
In dem Artikel haben wir ausführlich über die Verwendung des App-Routers für die Zustandsverwaltung gesprochen, der in Next.js 13 eingeführt und erst später, insbesondere in Next.js 13.4, offiziell als stabil deklariert wurde.
Überraschenderweise wurde zwei Monate nach dieser Ankündigung ein offizieller Blog-Post veröffentlicht, in dem die weiteren Verbesserungen in Bezug auf Leistung, STABILITÄT und Entwicklererfahrung hervorgehoben werden.
Wie ich am Anfang des Artikels erwähnt habe, ist die neueste Version von Next.js eine 14, bei der die vielleicht bedeutendste Änderung die Stabilisierung der Serveraktionen war, die in Version 13 versprochen wurde. Um eine leicht säuerliche Note hinzuzufügen (da es bisher völlig unnötig war), war der Übergang von 13 zu 14 fließend. Wäre es anders gewesen, wäre ich ziemlich enttäuscht gewesen, die Hauptversion eines Frameworks für so wenige neue Funktionen zu aktualisieren, die in der Regel von Breaking Changes begleitet werden. Wie zu erwarten, ist das in Next.js 13 eingeführte Turbopack noch nicht produktionsreif.
Fassen wir zusammen, was wir gerade gelesen haben
Informationen zu den Herausforderungen im Zusammenhang mit der Verwendung des App-Routers, der mit Next.js 13 eingeführt wurde. So nutzen Sie die integrierten Hooks von React für die Zustandsverwaltung sowie eine der bekanntesten State-Management-Bibliotheken von Drittanbietern, wie z. B. Redux. Wir haben die Verwendung von Layout- und Seitendateien gesehen, und wir haben die Verwendung von Server- und Clientkomponenten angesprochen.
Ich hoffe, dass Sie nach dem Lesen des Artikels die Handhabung des Zustands mit dem Next.js Framework einfacher finden. Vielleicht gefällt es Ihnen sogar, wenn alles gut geht.