Capítulo 7 de 456
Como buscar dados em Server Components (fetch API, ORM) e Client Components (use API, SWR/React Query), e como fazer streaming de conteúdo que depende de dados lentos via loading.js ou <Suspense>.
fetch em Server Component: componente vira async function e usa await fetch(...); requisições idênticas na mesma árvore React são memoizadas automaticamente por padrão.loading.js (rota inteira) ou <Suspense> (granular).loading.js: envolve automaticamente page.js (e filhos) numa <Suspense> boundary, aninhado dentro de layout.js.<Suspense fallback={...}>: permite mostrar parte da página imediatamente e estriar só o trecho dinâmico dentro da boundary.use API (React): hook para ler uma Promise em Client Component; a Promise é criada (não awaitada) no Server Component e passada como prop.React.cache: memoiza uma função de fetch por request, múltiplas chamadas no mesmo request retornam o mesmo resultado sem refetch; escopo é por request, sem compartilhamento entre requests.Playlists precisa do artistID de getArtist); requests paralelos são iniciados juntos via Promise.all.// app/blog/page.tsx
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
// app/blog/page.tsx
import { Suspense } from 'react'
import BlogList from '@/components/BlogList'
import BlogListSkeleton from '@/components/BlogListSkeleton'
export default function BlogPage() {
return (
<div>
<header><h1>Welcome to the Blog</h1></header>
<main>
<Suspense fallback={<BlogListSkeleton />}>
<BlogList />
</Suspense>
</main>
</div>
)
}
// app/ui/posts.tsx
'use client'
import { use } from 'react'
export default function Posts({ posts }: { posts: Promise<{ id: string; title: string }[]> }) {
const allPosts = use(posts)
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
use() em Client Component.// app/lib/user.ts
import { cache } from 'react'
export const getUser = cache(async () => {
const res = await fetch('https://api.example.com/user')
return res.json()
})
React.cache, evitando refetch entre componentes.// app/artist/[username]/page.tsx
export default async function Page({ params }) {
const { username } = await params
const artistData = getArtist(username)
const albumsData = getAlbums(username)
const [artist, albums] = await Promise.all([artistData, albumsData])
return (
<>
<h1>{artist.name}</h1>
<Albums list={albums} />
</>
)
}
| Método | Onde | Característica |
|---|---|---|
fetch API | Server Component | memoizado por padrão, não cacheado entre requests sem use cache |
| ORM/DB client | Server Component | credenciais nunca vão ao cliente |
use API | Client Component | lê Promise vinda do servidor, precisa de <Suspense> |
| SWR / React Query | Client Component | cache/streaming próprios, fetch direto do browser |
await em cascata sem necessidade: getAlbums esperando getArtist terminar quando os dois são independentes, desperdiça tempo, use Promise.all para iniciar juntos.Promise.all quando uma falha não deve derrubar tudo: usar Promise.allSettled para tolerar falha parcial.await getPosts() antes de passar para Client Component: perde o streaming, passe a Promise sem await e resolva com use() dentro do Client Component.fetch ou ORM direto; fetch idêntico na mesma árvore é memoizado automaticamente.loading.js (rota inteira, <Suspense> implícito) ou <Suspense> explícito (granular); a primeira request lenta bloqueia tudo que está fora da boundary.Promise.all, não encadeados com await sequencial.React.cache deduplica fetches idênticos dentro do mesmo request, mas não compartilha nada entre requests diferentes.use(), mantendo o streaming do servidor até o cliente.revalidatePath/revalidateTag invalidam o que foi buscado aqui.