Next.js 15 App Router ve Server Components

Ders 3/5 35 dakika

Server Actions ile Form Yönetimi

React 19 Actions ile client-side JavaScript olmadan form submit, Progressive Enhancement ve optimistic updates.

Server Actions Nedir?

Server Actions, form submit işlemlerinin doğrudan sunucuda çalışmasını sağlar. API endpoint yazmadan, client-side JavaScript olmadan form verilerini işleyebilirsiniz.

Temel Server Action

// app/iletisim/page.tsx
export default function IletisimFormu() {

  // Server Action — "use server" direktifi ile
  async function formuGonder(formData: FormData) {
    "use server";

    const isim = formData.get("isim") as string;
    const mesaj = formData.get("mesaj") as string;

    // Sunucuda çalışır — DB, email, vb.
    await emailGonder({ isim, mesaj });

    console.log(`Mesaj alındı: ${isim} — ${mesaj}`);
  }

  return (
    <form action={formuGonder}>
      <input name="isim" placeholder="Adınız" required />
      <textarea name="mesaj" placeholder="Mesajınız" required />
      <button type="submit">Gönder</button>
    </form>
  );
}

useActionState ile Durum Yönetimi

"use client";
import { useActionState } from "react";

async function girisYap(prevState: any, formData: FormData) {
  "use server";
  const email = formData.get("email") as string;
  const sifre = formData.get("sifre") as string;

  if (sifre.length < 8) {
    return { hata: "Şifre en az 8 karakter olmalı" };
  }

  // Giriş işlemi...
  return { basari: true };
}

export default function GirisFormu() {
  const [durum, action, bekliyor] = useActionState(girisYap, null);

  return (
    <form action={action}>
      {durum?.hata && (
        <p className="text-red-500">{durum.hata}</p>
      )}
      {durum?.basari && (
        <p className="text-green-500">Giriş başarılı!</p>
      )}
      <input name="email" type="email" required />
      <input name="sifre" type="password" required />
      <button disabled={bekliyor}>
        {bekliyor ? "Giriş yapılıyor..." : "Giriş Yap"}
      </button>
    </form>
  );
}

useOptimistic ile Anlık UI Güncellemesi

"use client";
import { useOptimistic } from "react";

export default function YorumListesi({ yorumlar }: { yorumlar: Yorum[] }) {
  const [optimistikYorumlar, yorumEkle] = useOptimistic(
    yorumlar,
    (mevcutYorumlar, yeniYorum: Yorum) => [...mevcutYorumlar, yeniYorum]
  );

  async function yorumGonder(formData: FormData) {
    "use server";
    const metin = formData.get("metin") as string;
    // Anında UI güncellenir, sunucu cevabını beklemez
    yorumEkle({ id: Date.now(), metin, bekliyor: true });
    await yorumuKaydet(metin);
  }

  return (
    <div>
      {optimistikYorumlar.map(y => (
        <p key={y.id} className={y.bekliyor ? "opacity-50" : ""}>
          {y.metin}
        </p>
      ))}
      <form action={yorumGonder}>
        <input name="metin" />
        <button>Yorum Yap</button>
      </form>
    </div>
  );
}

Ayrı actions.ts Dosyası

// app/actions.ts
"use server";

import { revalidatePath } from "next/cache";

export async function urunEkle(formData: FormData) {
  const isim = formData.get("isim") as string;
  await veritabanineEkle({ isim });
  revalidatePath("/urunler"); // Cache temizle, sayfayı yenile
}

export async function urunSil(id: number) {
  await veritabanindenSil(id);
  revalidatePath("/urunler");
}

Önemli Noktalar

  • "use server" direktifi ile Server Action tanımlanır
  • form action'ına Server Action atanır — API endpoint gerekmez
  • useActionState ile form durumu (hata/başarı/bekliyor) yönetilir
  • revalidatePath ile önbellek temizlenerek sayfa yenilenir