React Server Components (RSC), React ekosisteminde devrim niteliğinde bir yenilik olarak karşımıza çıkıyor. Bu yazıda, RSC'nin ne olduğunu, nasıl kullanılacağını ve modern web uygulamalarına getirdiği avantajları detaylıca inceleyeceğiz.
React Server Components Nedir?
React Server Components, sunucu tarafında render edilen ve istemci tarafında hidrasyona ihtiyaç duymayan özel React bileşenleridir. Bu yaklaşım, geleneksel client-side rendering ve server-side rendering yaklaşımlarının en iyi yanlarını birleştirir.
Server Components vs Client Components
// server-component.jsx // 'use server' direktifi ile server component olduğunu belirtiyoruz 'use server'; async function ProductList() { // Veritabanından ürünleri çek const products = await db.products.findMany(); return ( <ul> {products.map(product => ( <li key={product.id}> {product.name} - {product.price}₺ </li> ))} </ul> ); } // client-component.jsx 'use client'; function AddToCartButton({ productId }) { const [isLoading, setIsLoading] = useState(false); const handleClick = async () => { setIsLoading(true); await addToCart(productId); setIsLoading(false); }; return ( <button onClick={handleClick} disabled={isLoading} > {isLoading ? 'Ekleniyor...' : 'Sepete Ekle'} </button> ); }
Server Components'in Avantajları
1. Daha İyi Performans
Server Components, bundle size'ı küçültür ve initial page load süresini iyileştirir:
// ❌ Client Component yaklaşımı import { format } from 'date-fns'; import { marked } from 'marked'; import highlight from 'highlight.js'; function BlogPost({ content, date }) { // Tüm dependencies client bundle'a dahil olur const formattedDate = format(date, 'dd/MM/yyyy'); const htmlContent = marked(content); return ( <article> <time>{formattedDate}</time> <div dangerouslySetInnerHTML={{ __html: htmlContent }} /> </article> ); } // ✅ Server Component yaklaşımı async function BlogPost({ id }) { // Dependencies sadece sunucuda kullanılır const post = await db.posts.findUnique({ where: { id } }); const formattedDate = format(post.date, 'dd/MM/yyyy'); const htmlContent = marked(post.content); return ( <article> <time>{formattedDate}</time> <div dangerouslySetInnerHTML={{ __html: htmlContent }} /> </article> ); }
2. Doğrudan Backend Erişimi
Server Components, API katmanına ihtiyaç duymadan doğrudan backend kaynaklarına erişebilir:
// api/products.ts yerine doğrudan server component içinde async function ProductDetails({ id }: { id: string }) { // Doğrudan veritabanı sorgusu const product = await prisma.product.findUnique({ where: { id }, include: { reviews: true, category: true } }); // Dosya sistemine erişim const productImages = await fs.readdir(`/public/images/products/${id}`); return ( <div> <h1>{product.name}</h1> <ProductGallery images={productImages} /> <ProductReviews reviews={product.reviews} /> </div> ); }
3. Otomatik Code Splitting
Server Components, otomatik code splitting sağlar:
// layout.jsx import { Suspense } from 'react'; export default function Layout({ children }) { return ( <div> <Header /> <Suspense fallback={<LoadingSpinner />}> {children} </Suspense> <Footer /> </div> ); } // page.jsx async function ProductPage({ id }) { // Bu komponentin kodu ve dependencies // sadece gerektiğinde yüklenir const product = await getProduct(id); return ( <div> <h1>{product.name}</h1> <Suspense fallback={<LoadingSpinner />}> <ProductReviews productId={id} /> </Suspense> </div> ); }
Streaming ve Suspense Entegrasyonu
Server Components, Suspense ile birlikte kullanılarak kademeli yükleme sağlar:
// page.jsx import { Suspense } from 'react'; import { ProductHeader, ProductDetails, RelatedProducts } from './components'; export default function ProductPage({ id }) { return ( <div> {/* Kritik içerik hemen yüklenir */} <ProductHeader id={id} /> {/* Detaylar stream edilir */} <Suspense fallback={<LoadingDetails />}> <ProductDetails id={id} /> </Suspense> {/* İlgili ürünler en son yüklenir */} <Suspense fallback={<LoadingRelated />}> <RelatedProducts id={id} /> </Suspense> </div> ); }
Error Handling
Server Components'te hata yönetimi:
// error-boundary.jsx 'use client'; import { Component } from 'react'; class ErrorBoundary extends Component { state = { hasError: false, error: null }; static getDerivedStateFromError(error) { return { hasError: true, error }; } render() { if (this.state.hasError) { return ( <div className="error-container"> <h2>Bir hata oluştu</h2> <button onClick={() => window.location.reload()}> Sayfayı Yenile </button> </div> ); } return this.props.children; } } // page.jsx export default function ProductPage({ id }) { return ( <ErrorBoundary> <Suspense fallback={<Loading />}> <ProductDetails id={id} /> </Suspense> </ErrorBoundary> ); }
Server Actions
Server Components ile birlikte gelen Server Actions özelliği:
// actions.ts 'use server'; export async function addToCart(productId: string, quantity: number) { const session = await getSession(); if (!session) { throw new Error('Oturum açmanız gerekiyor'); } try { await prisma.cart.upsert({ where: { userId_productId: { userId: session.user.id, productId } }, update: { quantity: { increment: quantity } }, create: { userId: session.user.id, productId, quantity } }); revalidatePath('/cart'); } catch (error) { throw new Error('Ürün sepete eklenirken bir hata oluştu'); } } // cart-button.tsx 'use client'; export function AddToCartButton({ productId }: { productId: string }) { const [isPending, startTransition] = useTransition(); return ( <button disabled={isPending} onClick={() => startTransition(() => addToCart(productId, 1))} > {isPending ? 'Ekleniyor...' : 'Sepete Ekle'} </button> ); }
Optimizasyon Stratejileri
1. Seçici Hidrasyon
// layout.jsx export default function Layout() { return ( <div> {/* Statik header server'da render edilir */} <Header /> {/* Interaktif search client'da hidrate edilir */} <SearchProvider> <Search /> </SearchProvider> {/* Ana içerik stream edilir */} <Suspense fallback={<Loading />}> <Content /> </Suspense> </div> ); }
2. Paralel Veri Fetching
// parallel-data.tsx async function ProductPage({ id }: { id: string }) { // Paralel veri çekme const [product, reviews, relatedProducts] = await Promise.all([ getProduct(id), getProductReviews(id), getRelatedProducts(id) ]); return ( <div> <ProductDetails product={product} /> <ReviewList reviews={reviews} /> <RelatedProducts products={relatedProducts} /> </div> ); }
Sonuç
React Server Components, modern web uygulamaları için önemli avantajlar sunar:
- Daha iyi ilk yükleme performansı
- Küçük bundle size
- Doğrudan backend erişimi
- Otomatik code splitting
- Gelişmiş streaming desteği
Bu özellikleri kullanarak, daha performanslı ve ölçeklenebilir React uygulamaları geliştirebilirsiniz.