React Server Components: Yeni Nesil React Uygulamaları

Yunus Emre Güzel
20 Ocak 202515 dkReact
React Server Components: Yeni Nesil React Uygulamaları

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.

Kaynaklar