React Server Components: Kapsamlı Rehber

React Server Components: Kapsamlı Rehber
React Server Components (RSC), React ekosisteminde devrim niteliğinde bir yeniliktir. Server tarafında render edilen, client'a JavaScript göndermeden çalışan componentler sayesinde uygulama performansınızı dramatik şekilde artırabilirsiniz. Bu rehberde RSC'nin çalışma mantığını, Next.js ile entegrasyonunu ve en iyi pratiklerini adım adım öğreneceksiniz.
React Server Components Nedir?
Server Components, React 18 ile tanıtılan ve özellikle Next.js 13+ App Router ile yaygınlaşan bir mimari yaklaşımdır. Bu componentler sunucuda çalışır, veritabanına doğrudan erişebilir ve client'a sadece render edilmiş HTML gönderir. Bu sayede bundle boyutu küçülür ve ilk sayfa yüklenme süresi (FCP) iyileşir.
Server vs Client Components
Next.js App Router'da varsayılan olarak tüm componentler Server Component'tir. Bir component'i Client Component yapmak için dosyanın başına "use client" direktifi eklemeniz gerekir. İşte temel farklar:
// app/components/ProductList.tsx — Server Component (varsayılan)
// "use client" yok, bu bir Server Component
import { db } from '@/lib/database';
interface Product {
id: string;
name: string;
price: number;
description: string;
}
export default async function ProductList() {
// Doğrudan veritabanı sorgusu — client'a SQL gönderilmez
const products: Product[] = await db.query('SELECT * FROM products WHERE active = true ORDER BY created_at DESC LIMIT 20');
return (
<section>
<h2>Ürünler</h2>
<div className="grid grid-cols-3 gap-4">
{products.map((product) => (
<article key={product.id} className="border rounded-lg p-4">
<h3>{product.name}</h3>
<p>{product.description}</p>
<span className="font-bold">{product.price} TL</span>
<AddToCartButton productId={product.id} />
</article>
))}
</div>
</section>
);
}
// app/components/AddToCartButton.tsx — Client Component
"use client";
import { useState } from 'react';
import { useCart } from '@/hooks/useCart';
interface AddToCartButtonProps {
productId: string;
}
export default function AddToCartButton({ productId }: AddToCartButtonProps) {
const [isAdding, setIsAdding] = useState(false);
const { addItem } = useCart();
const handleClick = async () => {
setIsAdding(true);
try {
await addItem(productId);
} finally {
setIsAdding(false);
}
};
return (
<button
onClick={handleClick}
disabled={isAdding}
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 disabled:opacity-50"
>
{isAdding ? 'Ekleniyor...' : 'Sepete Ekle'}
</button>
);
}
Server Actions ile Form İşleme
React Server Actions, form submit işlemlerini sunucu tarafında işlemenin modern yoludur. API route oluşturmadan doğrudan sunucu fonksiyonlarını çağırabilirsiniz.
// app/actions/contact.ts
"use server";
import { z } from 'zod';
import { db } from '@/lib/database';
import { revalidatePath } from 'next/cache';
const contactSchema = z.object({
name: z.string().min(2, 'İsim en az 2 karakter olmalıdır'),
email: z.string().email('Geçerli bir e-posta adresi giriniz'),
message: z.string().min(10, 'Mesaj en az 10 karakter olmalıdır'),
});
export async function submitContactForm(formData: FormData) {
const rawData = {
name: formData.get('name') as string,
email: formData.get('email') as string,
message: formData.get('message') as string,
};
const validated = contactSchema.safeParse(rawData);
if (!validated.success) {
return {
success: false,
errors: validated.error.flatten().fieldErrors,
};
}
await db.insert('contacts', validated.data);
revalidatePath('/contact');
return { success: true, errors: null };
}
// app/contact/page.tsx — Server Component
import { submitContactForm } from '@/actions/contact';
import ContactForm from './ContactForm';
export default function ContactPage() {
return (
<main>
<h1>İletişim</h1>
<ContactForm action={submitContactForm} />
</main>
);
}
Veri Çekme ve Caching Stratejileri
Server Components'te veri çekme işlemleri doğrudan component içinde yapılır. Next.js, fetch isteklerini otomatik olarak önbelleğe alır ve revalidation stratejileri sunar.
// app/blog/page.tsx
interface Post {
id: string;
title: string;
excerpt: string;
publishedAt: string;
}
// Statik — build zamanında çekilir, cache'lenir
async function getStaticPosts(): Promise<Post[]> {
const res = await fetch('https://api.example.com/posts', {
cache: 'force-cache', // Varsayılan
});
return res.json();
}
// Zamana dayalı revalidation — 60 saniyede bir yenilenir
async function getRevalidatedPosts(): Promise<Post[]> {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 },
});
return res.json();
}
// Dinamik — her istekte yeniden çekilir
async function getDynamicPosts(): Promise<Post[]> {
const res = await fetch('https://api.example.com/posts', {
cache: 'no-store',
});
return res.json();
}
export default async function BlogPage() {
const posts = await getRevalidatedPosts();
return (
<main>
<h1>Blog</h1>
{posts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
<time>{new Date(post.publishedAt).toLocaleDateString('tr-TR')}</time>
</article>
))}
</main>
);
}
Composition Pattern: Server ve Client Birlikte
En etkili RSC kullanımı, Server ve Client componentlerini doğru şekilde birleştirmekten geçer. Genel kural olarak, veri çekme ve statik içerik Server'da, etkileşim (tıklama, form, animasyon) Client'ta kalmalıdır. Server Component'ler children prop'u ile Client Component'leri sarabilir — bu pattern, gereksiz client JavaScript'i göndermekten kaçınmanızı sağlar.
Sonuç
React Server Components, web geliştirmede yeni bir paradigma sunuyor. Doğru kullanıldığında bundle boyutunu %30-50 küçültebilir, ilk yüklenme süresini dramatik şekilde iyileştirebilir ve geliştirici deneyimini artırabilir. Server'da veri çekme, Client'ta etkileşim prensibini benimseyerek uygulamalarınızı hem hızlı hem de kullanıcı dostu yapabilirsiniz. Ancak bu yeni modelin öğrenme eğrisi olduğunu ve mevcut React bilginizi genişletmeniz gerektiğini unutmayın.