Capítulo 8 de 456

Mutating Data

Core Idea

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.

Key Concepts

  • Server Function: função assíncrona que roda no servidor, chamável do cliente via network request; só o método POST pode invocá-la.
  • Server Action: Server Function usada com 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).
  • Segurança obrigatória: Server Functions são acessíveis via POST direto, fora da UI da aplicação; toda função deve verificar autenticação/autorização internamente.
  • Progressive enhancement: forms com Server Actions em Server Components funcionam mesmo sem JS carregado; em Client Components, submissões são enfileiradas até a hidratação.
  • Dispatch sequencial: o cliente despacha e aguarda Server Functions uma de cada vez (não paralelo); para trabalho paralelo, usar fetching em Server Components ou paralelizar dentro da própria função/Route Handler.
  • 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.

Code Examples

// 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
}
  • O que demonstra: Server Function com verificação de autorização obrigatória antes de mutar.
// 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>
  )
}
  • O que demonstra: invocar Server Function via 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>
  )
}
  • O que demonstra: pending state de uma Server Function via 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')
}
  • O que demonstra: ordem correta, revalidar antes de redirecionar (código após redirect não executa).

Reference Tables

Onde invocar Server FunctionMecanismo
Server ou Client Component<form action={fn}> / <button formAction={fn}>
Client Componentevent handler (onClick)
Client ComponentuseEffect (ex.: incrementar view count no mount)
FunçãoEfeito
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

Anti-patterns

  • Confiar só na UI para proteger uma Server Function: ela é alcançável via POST direto; toda função precisa checar sessão/permissão internamente, não apenas confiar que só o form chama.
  • Colocar código após redirect() esperando que rode: redirect lança exceção, nada depois executa; revalidação deve vir antes.
  • Esperar paralelismo automático entre múltiplas Server Functions disparadas do cliente: o dispatch é sequencial por design atual; para paralelizar, mover o trabalho para dentro de uma função só ou para Route Handler.
  • Definir Server Function dentro de um Client Component: não é possível; deve ser importada de um arquivo/função com "use server".

Key Takeaways

  1. Server Action é uma Server Function usada com action/formAction em formulário; toda Server Function é chamável via POST direto e deve validar auth internamente.
  2. "use server" no topo da função marca só ela; no topo do arquivo marca todos os exports.
  3. Client Components não podem definir Server Functions, só importá-las de um arquivo "use server" e invocá-las via form, prop ou event handler.
  4. Após mutação: refresh() atualiza a UI sem revalidar tags, revalidatePath/revalidateTag invalidam cache, redirect() interrompe a execução (chame revalidação antes).
  5. cookies().set()/delete() dentro de uma Server Action força re-render da página e layouts atuais no servidor, preservando estado client-side.

Connects To

  • Fetching Data: revalidatePath/revalidateTag invalidam exatamente o que foi buscado com fetch/ORM na página anterior.
  • Server and Client Components: a diretiva "use server" espelha o mesmo conceito de module boundary visto em "use client".