Formulare und Mutationen
Formulare ermöglichen Ihnen das Erstellen und Aktualisieren von Daten in Webanwendungen. Next.js bietet eine leistungsstarke Möglichkeit, Formularübermittlungen und Datenmutationen mit Server Actions zu handhaben.
Beispiele
Wie Server Actions funktionieren
Mit Server Actions müssen Sie keine manuellen API-Endpunkte erstellen. Stattdessen definieren Sie asynchrone Serverfunktionen, die direkt aus Ihren Komponenten aufgerufen werden können.
🎥 Ansehen: Erfahren Sie mehr über Formulare und Mutationen mit dem App-Router → YouTube (10 Minuten).
Server Actions können in Server-Komponenten definiert oder aus Client-Komponenten aufgerufen werden. Die Definition der Aktion in einer Server-Komponente ermöglicht es dem Formular, ohne JavaScript zu funktionieren, was eine progressive Verbesserung bietet.
Aktivieren Sie Server Actions in Ihrer next.config.js
-Datei:
module.exports = {
experimental: {
serverActions: true,
},
}
Gut zu wissen:
- Formulare, die Server Actions aus Server-Komponenten aufrufen, können ohne JavaScript funktionieren.
- Formulare, die Server Actions aus Client-Komponenten aufrufen, werden bei noch nicht geladenem JavaScript in eine Warteschlange gestellt, wobei die Client-Hydration priorisiert wird.
- Server Actions erben die Laufzeitumgebung von der Seite oder dem Layout, in dem sie verwendet werden.
- Server Actions funktionieren mit vollständig statischen Routen (einschließlich der Revalidierung von Daten mit ISR).
Revalidierung von gecachten Daten
Server Actions sind tief in die Next.js Caching- und Revalidierungsarchitektur integriert. Wenn ein Formular übermittelt wird, kann die Server Action gecachte Daten aktualisieren und alle Cache-Schlüssel revalidieren, die sich ändern sollten.
Im Gegensatz zu traditionellen Anwendungen, die auf ein Formular pro Route beschränkt sind, ermöglichen Server Actions mehrere Aktionen pro Route. Darüber hinaus muss der Browser bei einer Formularübermittlung nicht aktualisiert werden. In einer einzigen Netzwerkrunde kann Next.js sowohl die aktualisierte Benutzeroberfläche als auch die aktualisierten Daten zurückgeben.
Sehen Sie sich die folgenden Beispiele für die Revalidierung von Daten aus Server Actions an.
Beispiele
Server-exklusive Formulare
Um ein server-exklusives Formular zu erstellen, definieren Sie die Server Action in einer Server-Komponente. Die Aktion kann entweder inline mit der "use server"
-Direktive am Anfang der Funktion oder in einer separaten Datei mit der Direktive am Anfang der Datei definiert werden.
export default function Page() {
async function create(formData: FormData) {
'use server'
// Daten mutieren
// Cache revalidieren
}
return <form action={create}>...</form>
}
export default function Page() {
async function create(formData) {
'use server'
// Daten mutieren
// Cache revalidieren
}
return <form action={create}>...</form>
}
Gut zu wissen:
<form action={create}>
verwendet den FormData-Datentyp. Im obigen Beispiel ist das über das HTML-form
übermittelte FormData in der Server-Actioncreate
zugänglich.
Revalidierung von Daten
Server Actions ermöglichen es Ihnen, den Next.js-Cache bei Bedarf zu invalidieren. Sie können ein gesamtes Routensegment mit revalidatePath
invalidieren:
'use server'
import { revalidatePath } from 'next/cache'
export default async function submit() {
await submitForm()
revalidatePath('/')
}
'use server'
import { revalidatePath } from 'next/cache'
export default async function submit() {
await submitForm()
revalidatePath('/')
}
Oder invalidieren Sie einen bestimmten Datenabruf mit einem Cache-Tag mithilfe von revalidateTag
:
'use server'
import { revalidateTag } from 'next/cache'
export default async function submit() {
await addPost()
revalidateTag('posts')
}
'use server'
import { revalidateTag } from 'next/cache'
export default async function submit() {
await addPost()
revalidateTag('posts')
}
Weiterleitung
Wenn Sie den Benutzer nach Abschluss einer Server Action zu einer anderen Route weiterleiten möchten, können Sie redirect
und eine beliebige absolute oder relative URL verwenden:
'use server'
import { redirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
export default async function submit() {
const id = await addPost()
revalidateTag('posts') // Gecachte Beiträge aktualisieren
redirect(`/post/${id}`) // Zu neuer Route navigieren
}
'use server'
import { redirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
export default async function submit() {
const id = await addPost()
revalidateTag('posts') // Gecachte Beiträge aktualisieren
redirect(`/post/${id}`) // Zu neuer Route navigieren
}
Formularvalidierung
Wir empfehlen die Verwendung von HTML-Validierung wie required
und type="email"
für grundlegende Formularvalidierung.
Für erweiterte serverseitige Validierung verwenden Sie eine Schema-Validierungsbibliothek wie zod, um die Struktur der geparsten Formulardaten zu validieren:
import { z } from 'zod'
const schema = z.object({
// ...
})
export default async function submit(formData: FormData) {
const parsed = schema.parse({
id: formData.get('id'),
})
// ...
}
import { z } from 'zod'
const schema = z.object({
// ...
})
export default async function submit(formData) {
const parsed = schema.parse({
id: formData.get('id'),
})
// ...
}
Anzeigen des Ladezustands
Verwenden Sie den useFormStatus
-Hook, um einen Ladezustand anzuzeigen, wenn ein Formular auf dem Server übermittelt wird. Der useFormStatus
-Hook kann nur als Kind eines form
-Elements verwendet werden, das eine Server Action verwendet.
Zum Beispiel der folgende Submit-Button:
<SubmitButton />
kann dann in einem Formular mit einer Server Action verwendet werden:
import { SubmitButton } from '@/app/submit-button'
export default async function Home() {
return (
<form action={...}>
<input type="text" name="field-name" />
<SubmitButton />
</form>
)
}
import { SubmitButton } from '@/app/submit-button'
export default async function Home() {
return (
<form action={...}>
<input type="text" name="field-name" />
<SubmitButton />
</form>
)
}
Fehlerbehandlung
Server Actions können auch serialisierbare Objekte zurückgeben. Beispielsweise könnte Ihre Server Action Fehler bei der Erstellung eines neuen Elements behandeln:
'use server'
export async function createTodo(prevState: any, formData: FormData) {
try {
await createItem(formData.get('todo'))
return revalidatePath('/')
} catch (e) {
return { message: 'Failed to create' }
}
}
'use server'
export async function createTodo(prevState, formData) {
try {
await createItem(formData.get('todo'))
return revalidatePath('/')
} catch (e) {
return { message: 'Failed to create' }
}
}
Dann können Sie in einer Client-Komponente diesen Wert auslesen und eine Fehlermeldung anzeigen.
'use client'
import { experimental_useFormState as useFormState } from 'react-dom'
import { experimental_useFormStatus as useFormStatus } from 'react-dom'
import { createTodo } from '@/app/actions'
const initialState = {
message: null,
}
function SubmitButton() {
const { pending } = useFormStatus()
return (
<button type="submit" aria-disabled={pending}>
Add
</button>
)
}
export function AddForm() {
const [state, formAction] = useFormState(createTodo, initialState)
return (
<form action={formAction}>
<label htmlFor="todo">Enter Task</label>
<input type="text" id="todo" name="todo" required />
<SubmitButton />
<p aria-live="polite" className="sr-only">
{state?.message}
</p>
</form>
)
}
'use client'
import { experimental_useFormState as useFormState } from 'react-dom'
import { experimental_useFormStatus as useFormStatus } from 'react-dom'
import { createTodo } from '@/app/actions'
const initialState = {
message: null,
}
function SubmitButton() {
const { pending } = useFormStatus()
return (
<button type="submit" aria-disabled={pending}>
Add
</button>
)
}
export function AddForm() {
const [state, formAction] = useFormState(createTodo, initialState)
return (
<form action={formAction}>
<label htmlFor="todo">Enter Task</label>
<input type="text" id="todo" name="todo" required />
<SubmitButton />
<p aria-live="polite" className="sr-only">
{state?.message}
</p>
</form>
)
}
Optimistische Updates
Verwenden Sie useOptimistic
, um die Benutzeroberfläche optimistisch zu aktualisieren, bevor die Server Action abgeschlossen ist, anstatt auf die Antwort zu warten:
'use client'
import { experimental_useOptimistic as useOptimistic } from 'react'
import { send } from './actions'
type Message = {
message: string
}
export function Thread({ messages }: { messages: Message[] }) {
const [optimisticMessages, addOptimisticMessage] = useOptimistic<Message[]>(
messages,
(state: Message[], newMessage: string) => [
...state,
{ message: newMessage },
]
)
return (
<div>
{optimisticMessages.map((m, k) => (
<div key={k}>{m.message}</div>
))}
<form
action={async (formData: FormData) => {
const message = formData.get('message')
addOptimisticMessage(message)
await send(message)
}}
>
<input type="text" name="message" />
<button type="submit">Send</button>
</form>
</div>
)
}
'use client'
import { experimental_useOptimistic as useOptimistic } from 'react'
import { send } from './actions'
export function Thread({ messages }) {
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [...state, { message: newMessage }]
)
return (
<div>
{optimisticMessages.map((m) => (
<div>{m.message}</div>
))}
<form
action={async (formData) => {
const message = formData.get('message')
addOptimisticMessage(message)
await send(message)
}}
>
<input type="text" name="message" />
<button type="submit">Send</button>
</form>
</div>
)
}
Cookies setzen
Sie können Cookies in einer Server Action mit der cookies
-Funktion setzen:
'use server'
import { cookies } from 'next/headers'
export async function create() {
const cart = await createCart()
cookies().set('cartId', cart.id)
}
'use server'
import { cookies } from 'next/headers'
export async function create() {
const cart = await createCart()
cookies().set('cartId', cart.id)
}
Cookies auslesen
Sie können Cookies in einer Server Action mit der cookies
-Funktion auslesen:
'use server'
import { cookies } from 'next/headers'
export async function read() {
const auth = cookies().get('authorization')?.value
// ...
}
'use server'
import { cookies } from 'next/headers'
export async function read() {
const auth = cookies().get('authorization')?.value
// ...
}
Cookies löschen
Sie können Cookies in einer Server Action mit der cookies
-Funktion löschen:
'use server'
import { cookies } from 'next/headers'
export async function delete() {
cookies().delete('name')
// ...
}
'use server'
import { cookies } from 'next/headers'
export async function delete() {
cookies().delete('name')
// ...
}
Weitere Beispiele zum Löschen von Cookies aus Server Actions finden Sie unter zusätzliche Beispiele.