Capítulo 8 de 456
Como mutar dados com React Server Functions (chamadas via "use server") e Server Actions, cobrindo criação, invocação por formulário/event handler, estados de pending, revalidação, redirecionamento e cookies.
POST pode invocá-la.startTransition em contexto de ação/mutação; acontece automaticamente quando passada ao action de <form> ou formAction de <button>."use server": diretiva no topo de uma função assíncrona (marca só ela) ou no topo de um arquivo (marca todos os exports).useActionState: hook React que retorna [state, action, pending], usado para mostrar loading state durante a execução.refresh() (next/cache): atualiza o router do cliente para refletir o estado mais recente, sem revalidar dados com tag.revalidatePath / revalidateTag: invalidam cache do Next.js após mutação, mostrando dados atualizados.redirect(): lança uma exceção de controle de fluxo tratada pelo framework; código após ela não executa, então revalidatePath/revalidateTag devem vir antes.cookies() em Server Action: get/set/delete; setar/deletar cookie faz o Next.js re-renderizar a página atual e layouts no servidor, preservando estado client-side e re-rodando effects cujas dependências mudaram.// app/lib/actions.ts
'use server'
export async function createPost(formData: FormData) {
const session = await auth()
if (!session?.user) {
throw new Error('Unauthorized')
}
const title = formData.get('title')
const content = formData.get('content')
// Mutate data
// Revalidate cache
}
// app/ui/form.tsx
import { createPost } from '@/app/actions'
export function Form() {
return (
<form action={createPost}>
<input type="text" name="title" />
<input type="text" name="content" />
<button type="submit">Create</button>
</form>
)
}
action de <form>, recebendo FormData automaticamente.// app/ui/button.tsx
'use client'
import { useActionState, startTransition } from 'react'
import { createPost } from '@/app/actions'
import { LoadingSpinner } from '@/app/ui/loading-spinner'
export function Button() {
const [state, action, pending] = useActionState(createPost, false)
return (
<button onClick={() => startTransition(action)}>
{pending ? <LoadingSpinner /> : 'Create Post'}
</button>
)
}
useActionState.// app/lib/actions.ts
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
const session = await auth()
if (!session?.user) throw new Error('Unauthorized')
// Mutate data
revalidatePath('/posts')
redirect('/posts')
}
redirect não executa).| Onde invocar Server Function | Mecanismo |
|---|---|
| Server ou Client Component | <form action={fn}> / <button formAction={fn}> |
| Client Component | event handler (onClick) |
| Client Component | useEffect (ex.: incrementar view count no mount) |
| Função | Efeito |
|---|---|
refresh() | atualiza router do cliente, não revalida tags |
revalidatePath(path) | invalida cache de uma rota |
revalidateTag(tag) / updateTag(tag) | invalida cache por tag |
redirect(path) | redireciona, lança exceção de controle de fluxo |
redirect() esperando que rode: redirect lança exceção, nada depois executa; revalidação deve vir antes."use server".action/formAction em formulário; toda Server Function é chamável via POST direto e deve validar auth internamente."use server" no topo da função marca só ela; no topo do arquivo marca todos os exports."use server" e invocá-las via form, prop ou event handler.refresh() atualiza a UI sem revalidar tags, revalidatePath/revalidateTag invalidam cache, redirect() interrompe a execução (chame revalidação antes).cookies().set()/delete() dentro de uma Server Action força re-render da página e layouts atuais no servidor, preservando estado client-side.revalidatePath/revalidateTag invalidam exatamente o que foi buscado com fetch/ORM na página anterior."use server" espelha o mesmo conceito de module boundary visto em "use client".