Back

Erros Comuns com React Server Components

Erros Comuns com React Server Components

Você adotou o Next.js App Router. Server Components são o padrão. Tudo deveria ser mais rápido, mais simples, mais eficiente.

Em vez disso, você está depurando incompatibilidades de hidratação às 2 da manhã, se perguntando por que seu bundle cresceu e questionando se aquela diretiva 'use client' está no lugar certo.

React Server Components representam uma mudança fundamental na forma como as aplicações React funcionam. A fronteira servidor/cliente não é mais apenas uma preocupação de deploy—é uma decisão arquitetural que você toma com cada componente. Errar nisso leva a bugs sutis, regressões de performance e código que luta contra o framework em vez de aproveitá-lo.

Este artigo cobre os erros mais comuns com React Server Components que vi em bases de código de produção e como evitá-los.

Pontos-Chave

  • Server Components são o padrão no Next.js App Router—empurre 'use client' o mais abaixo possível na árvore de componentes para minimizar o tamanho do bundle.
  • Use o pacote server-only para prevenir a exposição acidental de código sensível do servidor no bundle do cliente.
  • Sempre converta valores não serializáveis (como funções e instâncias de classe) antes de passá-los de Server Components para Client Components.
  • Server Actions ('use server') são endpoints estilo RPC, não Server Components—valide todas as entradas e nunca confie em dados do cliente.
  • Seja explícito sobre o comportamento de cache com revalidate ou cache: 'no-store', já que os padrões do Next.js mudaram entre versões.

Entendendo Server vs Client Components

Antes de mergulhar nas armadilhas, vamos estabelecer a linha de base. No App Router, componentes são Server Components por padrão. Eles executam no servidor, não têm acesso a APIs do navegador e enviam zero JavaScript para o cliente.

Client Components requerem a diretiva 'use client'. Eles podem usar hooks como useState e useEffect, acessar APIs do navegador e lidar com interações do usuário.

A fronteira entre eles é onde a maioria dos erros acontece.

Uso Excessivo da Diretiva ‘use client’

A armadilha mais frequente do Next.js App Router com RSC é recorrer ao 'use client' cedo demais. Um componente precisa de useState? Marque-o como client component. Precisa de um handler onClick? Client component.

O problema: 'use client' cria uma fronteira. Tudo que esse componente importa se torna parte do bundle do cliente, mesmo que essas importações pudessem ter permanecido no servidor.

// ❌ A página inteira se torna um client component
'use client'

import { useState } from 'react'

export default function ProductPage({ product }) {
  const [quantity, setQuantity] = useState(1)
  
  return (
    <div>
      <ProductDetails product={product} />
      <ProductReviews productId={product.id} />
      <QuantitySelector value={quantity} onChange={setQuantity} />
    </div>
  )
}
// ✅ Apenas a peça interativa é um client component
import ProductDetails from './ProductDetails'
import ProductReviews from './ProductReviews'
import QuantitySelector from './QuantitySelector'

export default function ProductPage({ product }) {
  return (
    <div>
      <ProductDetails product={product} />
      <ProductReviews productId={product.id} />
      <QuantitySelector /> {/* Este é o único client component */}
    </div>
  )
}

Empurre 'use client' o mais abaixo possível na árvore de componentes. Isole a interatividade nos menores componentes que precisam dela.

Importar Código Exclusivo do Servidor em Client Components

Quando um client component importa um módulo, esse módulo inteiro (e suas dependências) é enviado para o navegador. Importou um cliente de banco de dados ou um arquivo que lê secrets de ambiente? Você acabou de expor código exclusivo do servidor no grafo do cliente.

// lib/db.js
import 'server-only' // Adicione isso para prevenir importações acidentais no cliente

export async function getUsers() {
  return db.query('SELECT * FROM users')
}

O pacote server-only (fornecido pelo Next.js) causa um erro de build se o módulo for importado em um client component. Use-o para qualquer código que nunca deve chegar ao navegador.

Passar Valores Não Serializáveis Através da Fronteira

Server Components passam props para Client Components através de serialização. Funções, instâncias de classe, Map e Set não podem cruzar essa fronteira.

// ❌ Instâncias de classe não são serializáveis
export default async function UserProfile({ userId }) {
  const user = await getUser(userId)
  return <ClientProfile user={user} /> // user é uma instância de classe
}

// ✅ Converta para um objeto simples
export default async function UserProfile({ userId }) {
  const user = await getUser(userId)
  return (
    <ClientProfile 
      user={{
        id: user.id,
        name: user.name,
        createdAt: user.createdAt.toISOString()
      }} 
    />
  )
}

Mal-Entendido sobre React Server Actions

A diretiva 'use server' marca funções como Server Actions—chamáveis do cliente mas executadas no servidor. Ela não torna um componente um Server Component. Server Components não precisam de nenhuma diretiva, pois são o padrão.

// Isso é uma Server Action, não um Server Component
async function submitForm(formData) {
  'use server'
  await db.insert({ email: formData.get('email') })
}

Server Actions são efetivamente endpoints estilo RPC. Trate-as como rotas de API: valide entradas, trate erros e nunca confie em dados do cliente.

Ignorar o Modelo de Cache do RSC

O comportamento de cache do Next.js evoluiu significativamente. Não assuma que chamadas fetch são cacheadas por padrão—isso varia por versão do Next.js, configuração de segmento de rota e configurações de runtime. Seja explícito sobre a atualização dos dados.

// Seja explícito sobre as intenções de cache
const data = await fetch(url, { 
  next: { revalidate: 3600 } // Cache por 1 hora
})

// Ou opte por não cachear
const data = await fetch(url, { cache: 'no-store' })

Use revalidatePath() e revalidateTag() em Server Actions para invalidar dados cacheados após mutações. O modelo de cache do RSC requer decisões intencionais sobre a atualização dos dados.

Conclusão

React Server Components recompensam o pensamento cuidadoso sobre onde o código executa. Use Server Components por padrão. Empurre as fronteiras do cliente para baixo. Serialize dados na borda. Valide entradas de Server Actions. Seja explícito sobre cache.

O modelo mental leva tempo para internalizar, mas a recompensa—bundles menores, carregamentos mais rápidos, busca de dados mais simples—vale o investimento.

Perguntas Frequentes

Parcialmente. Server Components não podem usar hooks de estado ou efeito como useState ou useEffect porque executam apenas no servidor. No entanto, hooks como useContext são suportados. Se seu componente precisa de estado, efeitos ou APIs do navegador, você deve adicionar a diretiva use client para torná-lo um Client Component. Mantenha essas peças interativas o menor e mais isoladas possível.

Pergunte-se se o componente precisa de interatividade, APIs do navegador ou React hooks como useState ou useEffect. Se sim, ele deve ser um Client Component. Se ele apenas renderiza dados ou busca de um banco de dados, mantenha-o como Server Component. Em caso de dúvida, comece com um Server Component e adicione use client apenas quando o build ou runtime explicitamente exigir.

A causa mais comum é colocar use client muito alto na sua árvore de componentes. Quando um componente pai se torna um Client Component, todas as suas importações entram no bundle do cliente. Audite suas diretivas use client e empurre-as para os menores componentes interativos. Também verifique importações acidentais de bibliotecas exclusivas do servidor em código do cliente.

A diretiva use client marca um componente para executar no navegador com acesso a hooks e APIs do navegador. A diretiva use server marca uma função como Server Action, que é chamável do cliente mas executa no servidor. Server Components não precisam de nenhuma diretiva, pois são o padrão no Next.js App Router.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay