Suche und Paginierung hinzufügen
Im vorherigen Kapitel haben Sie die Ladeleistung Ihres Dashboards durch Streaming verbessert. Nun wenden wir uns der Seite /invoices
zu und lernen, wie man Suche und Paginierung hinzufügt.
Startcode
Fügen Sie folgenden Code in Ihre Datei /dashboard/invoices/page.tsx
ein:
Nehmen Sie sich Zeit, um sich mit der Seite und den Komponenten vertraut zu machen:
<Search/>
ermöglicht Benutzern die Suche nach bestimmten Rechnungen.<Pagination/>
erlaubt die Navigation zwischen Rechnungsseiten.<Table/>
zeigt die Rechnungen an.
Die Suchfunktion erstreckt sich über Client und Server. Wenn ein Benutzer eine Rechnung sucht, werden die URL-Parameter aktualisiert, Daten auf dem Server abgerufen und die Tabelle mit den neuen Daten neu gerendert.
Warum URL-Suchparameter verwenden?
Wie erwähnt, werden URL-Suchparameter zur Verwaltung des Suchstatus verwendet. Dieses Muster könnte neu sein, wenn Sie es gewohnt sind, clientseitigen State zu verwenden.
Vorteile der Implementierung mit URL-Parametern:
- Lesezeichenfreundliche und teilbare URLs: Da die Suchparameter in der URL sind, können Benutzer den aktuellen Zustand der Anwendung inklusive Suchanfragen und Filtern als Lesezeichen speichern oder teilen.
- Server-seitiges Rendering (SSR): URL-Parameter können direkt auf dem Server verarbeitet werden, um den initialen Zustand zu rendern.
- Analytics und Tracking: Suchanfragen und Filter in der URL erleichtern die Verfolgung des Benutzerverhaltens ohne zusätzliche clientseitige Logik.
Hinzufügen der Suchfunktion
Folgende Next.js Client-Hooks werden für die Suchfunktion verwendet:
useSearchParams
- Ermöglicht den Zugriff auf die Parameter der aktuellen URL. Beispielsweise sehen die Suchparameter für diese URL/dashboard/invoices?page=1&query=pending
so aus:{page: '1', query: 'pending'}
.usePathname
- Liest den aktuellen URL-Pfad. Für die Route/dashboard/invoices
gibtusePathname
'/dashboard/invoices'
zurück.useRouter
- Ermöglicht die programmatische Navigation zwischen Routen in Client-Komponenten. Es stehen mehrere Methoden zur Verfügung.
Übersicht der Implementierungsschritte:
- Benutzereingabe erfassen.
- URL mit Suchparametern aktualisieren.
- URL mit dem Eingabefeld synchron halten.
- Tabelle entsprechend der Suchanfrage aktualisieren.
1. Benutzereingabe erfassen
Gehen Sie zur <Search>
-Komponente (/app/ui/search.tsx
). Dort sehen Sie:
"use client"
- Dies ist eine Client-Komponente, daher können Event-Listener und Hooks verwendet werden.<input>
- Das Suchfeld.
Erstellen Sie eine neue handleSearch
-Funktion und fügen Sie einen onChange
-Listener zum <input>
-Element hinzu. onChange
ruft handleSearch
bei Änderungen des Eingabewerts auf.
Überprüfen Sie die Funktionsweise in den Browser-Entwicklertools. Bei Eingabe in das Suchfeld sollte der Suchbegriff in der Konsole erscheinen.
Gut! Die Benutzereingabe wird erfasst. Nun muss die URL mit dem Suchbegriff aktualisiert werden.
2. URL mit Suchparametern aktualisieren
Importieren Sie den useSearchParams
-Hook aus next/navigation
und weisen Sie ihn einer Variable zu:
Erstellen Sie in handleSearch
eine neue URLSearchParams
-Instanz mit der searchParams
-Variable.
URLSearchParams
ist eine Web-API zur Manipulation von URL-Abfrageparametern. Anstatt komplexe String-Literale zu erstellen, können Sie damit den Parameter-String wie ?page=1&query=a
erhalten.
Setzen Sie den Parameter-String basierend auf der Benutzereingabe. Bei leerer Eingabe sollte er gelöscht werden:
Mit dem Abfrage-String können Sie die URL mit den Next.js-Hooks useRouter
und usePathname
aktualisieren.
Importieren Sie useRouter
und usePathname
aus 'next/navigation'
und verwenden Sie die replace
-Methode von useRouter()
in handleSearch
:
Hier die Erklärung:
${pathname}
ist der aktuelle Pfad, z.B."/dashboard/invoices"
.params.toString()
wandelt die Eingabe in ein URL-freundliches Format um.replace(${pathname}?${params.toString()})
aktualisiert die URL mit den Suchdaten, z.B./dashboard/invoices?query=lee
bei der Suche nach "Lee".- Die URL wird ohne Seitenneuladen aktualisiert dank clientseitiger Navigation von Next.js (wie im Kapitel Navigieren zwischen Seiten beschrieben).
3. URL und Eingabe synchron halten
Um sicherzustellen, dass das Eingabefeld mit der URL synchronisiert ist und bei geteilten URLs ausgefüllt wird, können Sie defaultValue
mit dem Wert aus searchParams
setzen:
defaultValue
vs.value
/ Uncontrolled vs. ControlledBei State-Verwaltung des Eingabewerts würden Sie das
value
-Attribut für eine controlled component verwenden. Hier verwaltet React den State.Da Sie jedoch keinen State verwenden, kann
defaultValue
genutzt werden. Das native Input-Element verwaltet seinen eigenen State, was in Ordnung ist, da die Suchanfrage in der URL gespeichert wird.
4. Tabelle aktualisieren
Abschließend muss die Tabellenkomponente die Suchanfrage widerspiegeln.
Gehen Sie zurück zur Rechnungsseite.
Page-Komponenten akzeptieren eine searchParams
-Prop, sodass Sie die aktuellen URL-Parameter an die <Table>
-Komponente übergeben können.
In der <Table>
-Komponente werden die Props query
und currentPage
an die Funktion fetchFilteredInvoices()
übergeben, die die passenden Rechnungen zurückgibt.
Testen Sie die Änderungen. Bei einer Suche wird die URL aktualisiert, eine neue Serveranfrage gesendet und nur passende Rechnungen zurückgegeben.
Wann
useSearchParams()
-Hook vs.searchParams
-Prop?Es wurden zwei Methoden zum Extrahieren von Suchparametern verwendet. Die Wahl hängt davon ab, ob Sie auf Client- oder Server-Seite arbeiten.
<Search>
ist eine Client-Komponente, daher wurde deruseSearchParams()
-Hook verwendet.<Table>
ist eine Server-Komponente, die eigene Daten abruft, daher können diesearchParams
von der Page an die Komponente übergeben werden.Allgemein gilt: Für den Zugriff auf Parameter vom Client aus den
useSearchParams()
-Hook verwenden, um Server-Roundtrips zu vermeiden.
Best Practice: Debouncing
Glückwunsch! Sie haben die Suche mit Next.js implementiert! Aber es gibt noch etwas, was Sie zur Optimierung tun können.
Fügen Sie in Ihrer handleSearch
-Funktion den folgenden console.log
hinzu:
Geben Sie dann "Delba" in Ihre Suchleiste ein und überprüfen Sie die Konsole in den Dev-Tools. Was passiert?
Sie aktualisieren die URL bei jedem Tastenanschlag und fragen somit bei jedem Tastenanschlag Ihre Datenbank ab! Dies ist kein Problem, da unsere Anwendung klein ist, aber stellen Sie sich vor, Ihre Anwendung hätte Tausende von Nutzern, die jeweils bei jedem Tastenanschlag eine neue Anfrage an Ihre Datenbank senden.
Debouncing ist eine Programmierpraxis, die die Rate begrenzt, mit der eine Funktion ausgelöst werden kann. In unserem Fall möchten Sie die Datenbank nur abfragen, wenn der Benutzer mit der Eingabe aufgehört hat.
So funktioniert Debouncing:
- Ereignis auslösen: Wenn ein Ereignis, das gedebounced werden soll (z.B. ein Tastenanschlag im Suchfeld), auftritt, startet ein Timer.
- Warten: Wenn ein neues Ereignis auftritt, bevor der Timer abläuft, wird der Timer zurückgesetzt.
- Ausführung: Wenn der Timer das Ende seines Countdowns erreicht, wird die gedebouncede Funktion ausgeführt.
Sie können Debouncing auf verschiedene Arten implementieren, einschließlich der manuellen Erstellung Ihrer eigenen Debounce-Funktion. Um die Dinge einfach zu halten, verwenden wir eine Bibliothek namens use-debounce
.
Installieren Sie use-debounce
:
Importieren Sie in Ihrer <Search>
-Komponente eine Funktion namens useDebouncedCallback
:
Diese Funktion umschließt den Inhalt von handleSearch
und führt den Code erst nach einer bestimmten Zeit aus, wenn der Benutzer mit der Eingabe aufgehört hat (300ms).
Geben Sie nun erneut in Ihre Suchleiste ein und öffnen Sie die Konsole in den Dev-Tools. Sie sollten Folgendes sehen:
Durch Debouncing können Sie die Anzahl der an Ihre Datenbank gesendeten Anfragen reduzieren und somit Ressourcen sparen.
Pagination hinzufügen
Nachdem Sie die Suchfunktion eingeführt haben, werden Sie feststellen, dass die Tabelle nur 6 Rechnungen gleichzeitig anzeigt. Dies liegt daran, dass die Funktion fetchFilteredInvoices()
in data.ts
maximal 6 Rechnungen pro Seite zurückgibt.
Durch das Hinzufügen von Pagination können Benutzer durch die verschiedenen Seiten navigieren, um alle Rechnungen anzuzeigen. Lassen Sie uns sehen, wie Sie Pagination mit URL-Parametern implementieren können, genau wie bei der Suche.
Navigieren Sie zur <Pagination/>
-Komponente und Sie werden feststellen, dass es sich um eine Client-Komponente handelt. Sie möchten keine Daten auf dem Client abrufen, da dies Ihre Datenbankgeheimnisse preisgeben würde (denken Sie daran, Sie verwenden keine API-Schicht). Stattdessen können Sie die Daten auf dem Server abrufen und sie der Komponente als Prop übergeben.
Importieren Sie in /dashboard/invoices/page.tsx
eine neue Funktion namens fetchInvoicesPages
und übergeben Sie das query
aus searchParams
als Argument:
fetchInvoicesPages
gibt die Gesamtanzahl der Seiten basierend auf der Suchanfrage zurück. Wenn es beispielsweise 12 Rechnungen gibt, die der Suchanfrage entsprechen, und jede Seite 6 Rechnungen anzeigt, dann wäre die Gesamtanzahl der Seiten 2.
Übergeben Sie als Nächstes das totalPages
-Prop an die <Pagination/>
-Komponente:
Navigieren Sie zur <Pagination/>
-Komponente und importieren Sie die Hooks usePathname
und useSearchParams
. Wir werden diese verwenden, um die aktuelle Seite zu erhalten und die neue Seite festzulegen. Stellen Sie sicher, dass Sie auch den Code in dieser Komponente entkommentieren. Ihre Anwendung wird vorübergehend nicht funktionieren, da Sie die <Pagination/>
-Logik noch nicht implementiert haben. Lassen Sie uns das jetzt tun!
Erstellen Sie als Nächstes eine neue Funktion innerhalb der <Pagination>
-Komponente namens createPageURL
. Ähnlich wie bei der Suche verwenden Sie URLSearchParams
, um die neue Seitenzahl festzulegen, und pathName
, um die URL-Zeichenkette zu erstellen.
Hier eine Zusammenfassung dessen, was passiert:
createPageURL
erstellt eine Instanz der aktuellen Suchparameter.- Dann aktualisiert es den "page"-Parameter auf die angegebene Seitenzahl.
- Schließlich konstruiert es die vollständige URL mit dem Pfadnamen und den aktualisierten Suchparametern.
Der Rest der <Pagination>
-Komponente befasst sich mit Styling und verschiedenen Zuständen (erste, letzte, aktive, deaktivierte usw.). Wir werden hier nicht ins Detail gehen, aber Sie können den Code gerne durchsehen, um zu sehen, wo createPageURL
aufgerufen wird.
Schließlich möchten Sie, wenn der Benutzer eine neue Suchanfrage eingibt, die Seitenzahl auf 1 zurücksetzen. Sie können dies tun, indem Sie die handleSearch
-Funktion in Ihrer <Search>
-Komponente aktualisieren:
Zusammenfassung
Glückwunsch! Sie haben gerade die Suche und Pagination mit URL-Suchparametern und Next.js-APIs implementiert.
Zusammenfassend haben Sie in diesem Kapitel:
- Suche und Pagination mit URL-Suchparametern anstelle von Client-State behandelt.
- Daten auf dem Server abgerufen.
- Den
useRouter
-Hook für flüssigere Client-seitige Übergänge verwendet.
Diese Muster unterscheiden sich von dem, was Sie möglicherweise gewohnt sind, wenn Sie mit Client-seitigem React arbeiten, aber hoffentlich verstehen Sie jetzt besser die Vorteile der Verwendung von URL-Suchparametern und der Verlagerung dieses States auf den Server.