Üretimde React Server Components: desenler ve tuzaklar
Üretimde React Server Components: savaşta sınanmış desenler, sık karşılaşılan anti-desenler ve sağlam Next.js 15 uygulamaları için hata ayıklama stratejileri.

React Server Components (RSC), Next.js 15'te sunucu tarafı render'ın işleyişini temelden değiştirir; ancak üretim ortamındaki kullanım, resmi dokümantasyonun her zaman ele almadığı tuzakları ortaya çıkarır. Bu makale, çalışan desenleri, kırılan desenleri ve sorunların üretime ulaşmadan önce nasıl teşhis edileceğini ele alır.
Bir Server Component yalnızca sunucuda çalışır ve tarayıcıya sıfır JavaScript gönderir. Bir Client Component ("use client" ile işaretlenmiş) her iki tarafta da çalışır. Kural: Client Components'i mümkün olduğunca küçük ve ağacın mümkün olduğunca alt seviyesinde tutmak.
Sunucu-istemci sınırı: boundary desenini anlamak
En yaygın RSC tuzağı, Server ve Client Components arasındaki sınırla ilgilidir. Bir bileşen "use client" direktifini taşıdığında, içe aktarılan tüm çocukları da Client Component haline gelir, direktif olmasa bile.
import { ProductDetails } from './ProductDetails'
import { AddToCartButton } from './AddToCartButton'
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const product = await getProduct(id)
return (
<div>
{/* Server Component: doğrudan DB erişimi */}
<ProductDetails product={product} />
{/* Client Component: izole etkileşim */}
<AddToCartButton productId={product.id} price={product.price} />
</div>
)
}'use client'
import { useState } from 'react'
export function AddToCartButton({ productId, price }: { productId: string; price: number }) {
const [adding, setAdding] = useState(false)
async function handleAdd() {
setAdding(true)
await fetch('/api/cart', {
method: 'POST',
body: JSON.stringify({ productId, quantity: 1 }),
})
setAdding(false)
}
return (
<button onClick={handleAdd} disabled={adding}>
{adding ? 'Ekleniyor...' : `Sepete ekle — $${price}`}
</button>
)
}Anahtar desen: verileri Server Component'tan Client Component'a serileştirilebilir prop'lar olarak geçirmek. Fonksiyonlar, sınıflar ve Date nesneleri bu sınırı geçemez.
Anti-desen: gereksiz Client Component sarmalayıcısı
Sık yapılan bir hata, Server Component çocuklarını saran bir Client Component oluşturmak ve böylece tüm alt ağacı istemci tarafına zorlamaktır.
'use client'
import { useState } from 'react'
// Tüm çocuk içerik istemci tarafına geçer
export function PageWrapper({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState('light')
return (
<div className={theme}>
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
Temayı değiştir
</button>
{children}
</div>
)
}Çözüm: Server Components'i children olarak geçirmek (slot deseni). Prop olarak geçirilen çocuklar, ebeveyn bir Client Component olsa bile Server Components olarak kalır. Yukarıdaki kod, children bir Server Component ebeveynden geldiği sürece doğru şekilde çalışır.
import { PageWrapper } from './PageWrapper'
import { HeavyServerContent } from './HeavyServerContent'
export default function Layout() {
return (
<PageWrapper>
{/* İstemci sarmalayıcısına rağmen Server Component olarak kalır */}
<HeavyServerContent />
</PageWrapper>
)
}Bu kompozisyon deseni, ağır içerik için sunucu render'ının faydalarını korurken sarmalayıcı seviyesinde etkileşimi mümkün kılar.
Asenkron veri işleme: bileşende fetch deseni
React 19 ve Next.js 15, Server Components içinde doğrudan async/await desteği sunar. Bu desen, eski getServerSideProps yaklaşımına kıyasla veri çekmeyi basitleştirir.
import { cache } from 'react'
// Aynı render içindeki özdeş çağrıları tekilleştirir
const getUser = cache(async (userId: string) => {
const res = await fetch(`https://api.example.com/users/${userId}`, {
next: { revalidate: 3600 }, // 1 saat cache
})
if (!res.ok) throw new Error('User not found')
return res.json()
})
export default async function UserProfile({ userId }: { userId: string }) {
const user = await getUser(userId)
return (
<section>
<h2>{user.name}</h2>
<p>{user.email}</p>
<p>Üyelik tarihi: {new Date(user.createdAt).toLocaleDateString('tr-TR')}</p>
</section>
)
}Üç kritik nokta:
- React'in
cache()fonksiyonu, tek bir sunucu render'ı sırasında özdeş çağrıları tekilleştirir next: { revalidate }, Next.js tarafında cache süresini kontrol eder- Asenkron bir Server Component'taki hatalar en yakın
error.tsx'i tetikler
Serileştirme tuzağı: sınırı geçemeyenler
Server ve Client Components arasında değişen verilerin JSON ile serileştirilebilir olması gerekir. Sessiz hatalara veya çökmelere yol açan durumlar şunlardır.
// TUZAK: serileştirilemez tipleri geçirmek
// Fonksiyon — çalışmaz
<ClientComp onSubmit={async (data) => { /* server action */ }} />
// Bunun yerine içe aktarılmış bir Server Action kullanmak
import { submitForm } from '@/lib/actions/form'
<ClientComp onSubmit={submitForm} />
// Date nesnesi — çalışmaz
<ClientComp createdAt={new Date()} />
// ISO string — çalışır
<ClientComp createdAt={new Date().toISOString()} />
// Map, Set, RegExp — çalışmaz
<ClientComp data={new Map([['key', 'value']])} />
// Düz nesne veya dizi — çalışır
<ClientComp data={{ key: 'value' }} />Server Actions ("use server" ile işaretli fonksiyonlar) istisnadır: Next.js bunları HTTP endpoint'lerine dönüştürdüğü için bir Client Component'a prop olarak geçirilebilirler.
React / Next.js mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Streaming ve Suspense: ilerlemeli yükleme desenleri
Suspense ile SSR streaming, HTML'i tarayıcıya ilerlemeli olarak gönderir. Optimal desen, her asenkron bölümün etrafında granüler Suspense sınırları kullanır.
import { Suspense } from 'react'
import { RevenueChart } from './RevenueChart'
import { RecentOrders } from './RecentOrders'
import { UserStats } from './UserStats'
export default function DashboardPage() {
return (
<div className="grid grid-cols-2 gap-6">
<Suspense fallback={<ChartSkeleton />}>
<RevenueChart />
</Suspense>
<Suspense fallback={<StatsSkeleton />}>
<UserStats />
</Suspense>
<Suspense fallback={<TableSkeleton />}>
<RecentOrders />
</Suspense>
</div>
)
}Her bölüm bağımsız olarak yüklenir. RevenueChart 3 saniye, UserStats 200 ms sürerse, istatistikler grafiği beklemeden anında görünür.
Bir Suspense sınırı içindeki içerik sunucuda render edilir ve ilk HTML'e dahil edilir. Crawler'lar tam içeriği görür. Streaming yalnızca tarayıcıya teslim hızını etkiler, SEO görünürlüğünü değil.
Üretimde hata ayıklama: RSC sorunlarını izleme
RSC hataları çoğu zaman karmaşıktır. Üç teşhis tekniği üretimde işe yarar.
1. Hidrasyon uyumsuzluklarını tespit etmek
'use client'
import { useEffect, useState } from 'react'
export function HydrationDebug() {
const [isClient, setIsClient] = useState(false)
useEffect(() => {
setIsClient(true)
}, [])
if (process.env.NODE_ENV !== 'development') return null
return (
<div style={{ position: 'fixed', bottom: 0, right: 0, padding: '4px 8px', fontSize: 12 }}>
{isClient ? 'Client' : 'Server'}
</div>
)
}2. RSC payload'ını loglamak
Next.js 15'te next.config.ts içinde RSC log'larını etkinleştirmek:
const nextConfig = {
logging: {
fetches: {
fullUrl: true, // Tam fetch URL'lerini gösterir
},
},
}
export default nextConfig3. Payload boyutunu kontrol etmek
Aşırı büyük bir RSC payload'ı (> 128 KB) performansı düşürür. DevTools'ta text/x-component content type'lı ağ isteklerini izlemek gerekir.
İleri desen: Server Actions ile kompozisyon
Server Actions ve Server Components birleşimi doğal bir CQRS deseni oluşturur: sunucuda okumalar (RSC), action'lar üzerinden yazmalar.
import { getTodos } from '@/lib/services/todo'
import { TodoForm } from './TodoForm'
import { deleteTodo } from '@/lib/actions/todo'
export default async function TodoList() {
const todos = await getTodos()
return (
<div>
<TodoForm />
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.title}
<form action={deleteTodo}>
<input type="hidden" name="id" value={todo.id} />
<button type="submit">Sil</button>
</form>
</li>
))}
</ul>
</div>
)
}'use server'
import { revalidatePath } from 'next/cache'
import { TodoService } from '@/lib/services/todo'
export async function deleteTodo(formData: FormData) {
const id = formData.get('id') as string
await TodoService.delete(id)
revalidatePath('/todos')
}revalidatePath çağrısı, tam sayfa yenilemesi olmadan güncellenmiş verilerle yeni bir Server Component render'ını tetikler.
Bu konularda daha derin mülakat hazırlığı için SharpSkill'deki Next.js Server Actions modülü ile Next.js Data Fetching modülünü incelemek faydalıdır. Resmi React dokümantasyonu Server Components spesifikasyonunun tamamını ele alır.
Sonuç
- Client Components'i bileşen ağacının altında küçük ve izole tutmak
- Bir istemci sarmalayıcısı içinde Server Components'i korumak için slot desenini (
children) kullanmak - Sunucu-istemci sınırını geçen prop'ların serileştirilebilirliğini her zaman doğrulamak
- Her bağımsız asenkron bölümün etrafına granüler Suspense sınırları yerleştirmek
- Üretimde RSC payload boyutunu izlemek (hedef < 128 KB)
- Doğal bir CQRS deseni için Server Components (okumalar) ve Server Actions (yazmalar) birleşimini kullanmak
- Tek bir sunucu render'ı içindeki istekleri tekilleştirmek için React'in
cache()fonksiyonunu kullanmak
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
İlgili makaleler

React 19: Produksiyonda Server Components - Eksiksiz Rehber
React 19 Server Components ile produksiyon ortaminda ustun performans. Mimari, kaliplar, streaming, onbellekleme ve optimizasyon teknikleri.

2026'da React Compiler: Otomatik Memoizasyon ve Teknik Mülakat Soruları
React Compiler hakkında kapsamlı rehber — otomatik memoizasyon, derleme hattı, React kuralları, ESLint entegrasyonu ve 2026 yılı React teknik mülakat soruları.

İleri Düzey React Hooks: Desenler ve Optimizasyonlar
Kanıtlanmış desenlerle ileri düzey React Hooks'a hakim olun. Custom hooks, optimize useEffect, useMemo, useCallback ve performans teknikleri.