İçeriğe geç
Sedat Demir
Geri dön

React 19'un Yeni Özellikleri: Actions, use() Hook ve Daha Fazlası

React 19'un Yeni Özellikleri: Actions, use() Hook ve Daha Fazlası

React ekibi, uzun süredir beklenen React 19 sürümünü yayınladı ve bu güncelleme, kütüphanenin tarihindeki en kapsamlı değişikliklerden birini temsil ediyor. Form yönetiminden veri çekmeye, metadata kontrolünden performans optimizasyonuna kadar pek çok alanda köklü iyileştirmeler sunan React 19, geliştiricilerin günlük iş akışlarını önemli ölçüde basitleştirmeyi hedefliyor.

Bu yazıda, React 19'un öne çıkan tüm yeniliklerini detaylı kod örnekleriyle birlikte inceleyeceğiz.


1. Actions: Form Yönetiminin Yeni Çağı

React 19'un belki de en çok konuşulan özelliği Actions konseptidir. Daha önce form gönderimlerini yönetmek için onSubmit event handler'ları, useState ile loading durumları ve manuel hata yönetimi gerekiyordu. Actions, tüm bu karmaşıklığı ortadan kaldırıyor.

Eski Yöntem (React 18)

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setIsLoading(true);
    setError(null);

    try {
      await loginUser({ email, password });
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={email} onChange={(e) => setEmail(e.target.value)} />
      <input value={password} onChange={(e) => setPassword(e.target.value)} />
      <button disabled={isLoading}>
        {isLoading ? 'Giriş yapılıyor...' : 'Giriş Yap'}
      </button>
      {error && <p className="error">{error}</p>}
    </form>
  );
}

Yeni Yöntem (React 19 ile Actions)

function LoginForm() {
  const [error, setError] = useState(null);

  async function loginAction(formData) {
    setError(null);
    const email = formData.get('email');
    const password = formData.get('password');

    try {
      await loginUser({ email, password });
    } catch (err) {
      setError(err.message);
    }
  }

  return (
    <form action={loginAction}>
      <input name="email" type="email" placeholder="E-posta" />
      <input name="password" type="password" placeholder="Şifre" />
      <button type="submit">Giriş Yap</button>
      {error && <p className="error">{error}</p>}
    </form>
  );
}

Fark ettiyseniz, e.preventDefault() çağrısına, useState ile kontrollü input yönetimine ve manuel loading state'e artık ihtiyaç yok. React, action prop'u ile form gönderimini otomatik olarak yönetiyor.


2. useActionState: Action'ları Bir Üst Seviyeye Taşımak

useActionState hook'u (eskiden useFormState olarak biliniyordu), action fonksiyonlarının durumunu yönetmek için tasarlandı. Loading durumu, hata mesajları ve önceki state'i tek bir hook üzerinden kontrol edebilirsiniz.

import { useActionState } from 'react';

async function addToCartAction(previousState, formData) {
  const productId = formData.get('productId');
  const quantity = Number(formData.get('quantity'));

  try {
    const result = await addToCart(productId, quantity);
    return { success: true, message: `${result.name} sepete eklendi!` };
  } catch (error) {
    return { success: false, message: error.message };
  }
}

function AddToCartForm({ productId }) {
  const [state, formAction, isPending] = useActionState(addToCartAction, null);

  return (
    <form action={formAction}>
      <input type="hidden" name="productId" value={productId} />
      <label>
        Adet:
        <input name="quantity" type="number" defaultValue={1} min={1} />
      </label>
      <button type="submit" disabled={isPending}>
        {isPending ? 'Ekleniyor...' : 'Sepete Ekle'}
      </button>
      {state?.success && <p className="success">{state.message}</p>}
      {state?.success === false && <p className="error">{state.message}</p>}
    </form>
  );
}

useActionState üç değer döndürür:


3. useFormStatus: Alt Bileşenlerde Form Durumunu Okumak

Bazen form durumunu, formun içindeki alt bileşenlerden okumak istersiniz. useFormStatus tam olarak bu ihtiyacı karşılıyor.

import { useFormStatus } from 'react-dom';

function SubmitButton({ children }) {
  const { pending, data, method, action } = useFormStatus();

  return (
    <button type="submit" disabled={pending}>
      {pending ? (
        <span className="spinner">⏳ İşleniyor...</span>
      ) : (
        children
      )}
    </button>
  );
}

function ContactForm() {
  async function sendMessage(formData) {
    await fetch('/api/contact', {
      method: 'POST',
      body: formData,
    });
  }

  return (
    <form action={sendMessage}>
      <input name="name" placeholder="Adınız" required />
      <input name="email" type="email" placeholder="E-postanız" required />
      <textarea name="message" placeholder="Mesajınız" required />
      <SubmitButton>Mesaj Gönder</SubmitButton>
    </form>
  );
}

Önemli not: useFormStatus hook'u, mutlaka bir <form> elementinin alt bileşeni içinde kullanılmalıdır. Doğrudan form bileşeninin kendisinde çalışmaz.


4. use() Hook: Veri Çekmenin Devrimsel Yolu

React 19'un en heyecan verici yeniliklerinden biri use() hook'udur. Diğer hook'lardan farklı olarak, use() koşullu ifadelerin ve döngülerin içinde çağrılabilir. Promise'leri ve Context'leri doğrudan okuyabilir.

Promise ile Kullanım

import { use, Suspense } from 'react';

function fetchUser(userId) {
  return fetch(`/api/users/${userId}`).then((res) => res.json());
}

function UserProfile({ userPromise }) {
  const user = use(userPromise);

  return (
    <div className="user-profile">
      <img src={user.avatar} alt={user.name} />
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
      <span className="email">{user.email}</span>
    </div>
  );
}

function UserPage({ userId }) {
  const userPromise = fetchUser(userId);

  return (
    <Suspense fallback={<div className="skeleton">Yükleniyor...</div>}>
      <UserProfile userPromise={userPromise} />
    </Suspense>
  );
}

Koşullu Kullanım

use() hook'unun en güçlü yanı, diğer hook'ların aksine koşullu olarak çağrılabilmesidir:

function UserDashboard({ userId, isAdmin }) {
  // Bu, useState veya useEffect ile YAPILAMAZ ama use() ile yapılabilir!
  if (isAdmin) {
    const adminData = use(fetchAdminDashboard(userId));
    return <AdminDashboard data={adminData} />;
  }

  const userData = use(fetchUserDashboard(userId));
  return <UserDashboard data={userData} />;
}

Context ile Kullanım

use() hook'u ayrıca useContext yerine de kullanılabilir:

import { use, createContext } from 'react';

const ThemeContext = createContext('light');

function ThemedButton() {
  const theme = use(ThemeContext);

  return (
    <button className={`btn btn-${theme}`}>
      Tema: {theme === 'light' ? '☀️ Açık' : '🌙 Koyu'}
    </button>
  );
}

5. useOptimistic: İyimser Güncellemeler

Kullanıcı deneyimini iyileştirmenin en etkili yollarından biri optimistic updates (iyimser güncellemeler) kullanmaktır. React 19, bu kalıbı kolaylaştırmak için useOptimistic hook'unu sunuyor.

import { useOptimistic, useState } from 'react';

function TodoList() {
  const [todos, setTodos] = useState([]);
  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (currentTodos, newTodo) => [
      ...currentTodos,
      { ...newTodo, id: Date.now(), isPending: true },
    ]
  );

  async function addTodoAction(formData) {
    const title = formData.get('title');
    const newTodo = { title, completed: false };

    // İyimser güncelleme: Sunucu yanıtını beklemeden UI'ı güncelle
    addOptimisticTodo(newTodo);

    // Gerçek API çağrısı
    const savedTodo = await saveTodoToServer(newTodo);
    setTodos((prev) => [...prev, savedTodo]);
  }

  return (
    <div>
      <form action={addTodoAction}>
        <input name="title" placeholder="Yeni görev..." required />
        <button type="submit">Ekle</button>
      </form>
      <ul>
        {optimisticTodos.map((todo) => (
          <li
            key={todo.id}
            style={{ opacity: todo.isPending ? 0.6 : 1 }}
          >
            {todo.title}
            {todo.isPending && <span className="badge">Kaydediliyor...</span>}
          </li>
        ))}
      </ul>
    </div>
  );
}

Bu yaklaşımla, kullanıcı bir görev eklediğinde liste anında güncellenir. Sunucu yanıtı gelene kadar öğe hafif saydam gösterilir ve "Kaydediliyor..." etiketi görünür.


6. Dokümana Metadata Desteği

React 19 öncesinde <title>, <meta> ve <link> etiketlerini yönetmek için react-helmet gibi üçüncü parti kütüphanelere ihtiyaç vardı. Artık bu etiketleri doğrudan bileşenlerinizde kullanabilirsiniz.

function BlogPost({ post }) {
  return (
    <article>
      <title>{post.title} | Blog</title>
      <meta name="description" content={post.excerpt} />
      <meta property="og:title" content={post.title} />
      <meta property="og:description" content={post.excerpt} />
      <meta property="og:image" content={post.coverImage} />
      <link rel="canonical" href={`https://example.com/blog/${post.slug}`} />

      <h1>{post.title}</h1>
      <p className="date">{post.publishedAt}</p>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  );
}

React, bu etiketleri otomatik olarak <head> bölümüne taşır (hoisting). Birden fazla bileşen aynı metadata etiketlerini tanımlasa bile, React bunları doğru şekilde birleştirir ve yönetir.


7. Stylesheet Yönetimi ve Önceliklendirme

React 19, CSS stylesheet'lerini bileşen bazında yönetmenize ve önceliklendirmenize olanak tanıyan yerleşik bir mekanizma sunuyor:

function ProductCard({ product }) {
  return (
    <>
      <link rel="stylesheet" href="/styles/card.css" precedence="default" />
      <link rel="stylesheet" href="/styles/product.css" precedence="high" />

      <div className="product-card">
        <img src={product.image} alt={product.name} />
        <h3>{product.name}</h3>
        <span className="price">{product.price} TL</span>
      </div>
    </>
  );
}

precedence prop'u, stylesheet'lerin DOM'daki sırasını kontrol eder. React, aynı stylesheet'in birden fazla kez eklenmesini önler ve Suspense ile birlikte çalışarak, stylesheet yüklenene kadar içeriğin gösterilmesini bekleyebilir.


8. ref Artık Bir Prop

React 19 ile birlikte, fonksiyon bileşenlerinde ref doğrudan bir prop olarak erişilebilir hale geldi. forwardRef kullanımına artık gerek yok.

Eski Yöntem (React 18)

import { forwardRef } from 'react';

const CustomInput = forwardRef(function CustomInput(props, ref) {
  return <input ref={ref} {...props} className="custom-input" />;
});

Yeni Yöntem (React 19)

function CustomInput({ ref, ...props }) {
  return <input ref={ref} {...props} className="custom-input" />;
}

// Kullanım aynı kalıyor:
function SearchBar() {
  const inputRef = useRef(null);

  return (
    <div>
      <CustomInput ref={inputRef} placeholder="Ara..." />
      <button onClick={() => inputRef.current.focus()}>Odaklan</button>
    </div>
  );
}

forwardRef gelecekteki sürümlerde kaldırılacak (deprecated) olarak işaretlendi. Mevcut kodlarınız çalışmaya devam edecek, ancak yeni projelerinizde doğrudan prop olarak ref kullanmanız önerilir.


9. ref Callback'lerinde Cleanup Fonksiyonu

React 19, ref callback'lerinden bir cleanup fonksiyonu döndürmenize izin veriyor. Bu, DOM elemanları kaldırıldığında temizlik işlemleri yapmak için oldukça kullanışlıdır:

function VideoPlayer({ src }) {
  return (
    <video
      ref={(element) => {
        if (element) {
          // Element mount edildiğinde
          const observer = new IntersectionObserver(
            ([entry]) => {
              if (entry.isIntersecting) {
                element.play();
              } else {
                element.pause();
              }
            },
            { threshold: 0.5 }
          );
          observer.observe(element);

          // Cleanup: Element unmount edildiğinde
          return () => {
            observer.disconnect();
          };
        }
      }}
      src={src}
      muted
      loop
    />
  );
}

10. React Compiler (Deneysel)

React 19 ile birlikte tanıtılan React Compiler, kodunuzu derleme aşamasında otomatik olarak optimize eder. useMemo, useCallback ve React.memo gibi manuel optimizasyon API'lerine olan ihtiyacı büyük ölçüde ortadan kaldırır.

Compiler Öncesi (Manuel Optimizasyon)

function ProductList({ products, category }) {
  const filteredProducts = useMemo(
    () => products.filter((p) => p.category === category),
    [products, category]
  );

  const handleAddToCart = useCallback(
    (productId) => {
      addToCart(productId);
    },
    []
  );

  return (
    <ul>
      {filteredProducts.map((product) => (
        <MemoizedProductCard
          key={product.id}
          product={product}
          onAddToCart={handleAddToCart}
        />
      ))}
    </ul>
  );
}

const MemoizedProductCard = React.memo(ProductCard);

Compiler Sonrası (Otomatik Optimizasyon)

// React Compiler tüm optimizasyonları otomatik yapar!
function ProductList({ products, category }) {
  const filteredProducts = products.filter((p) => p.category === category);

  const handleAddToCart = (productId) => {
    addToCart(productId);
  };

  return (
    <ul>
      {filteredProducts.map((product) => (
        <ProductCard
          key={product.id}
          product={product}
          onAddToCart={handleAddToCart}
        />
      ))}
    </ul>
  );
}

Compiler, bileşenlerinizi analiz ederek hangi değerlerin yeniden hesaplanması gerektiğini otomatik olarak belirler ve gerekli memoizasyonu arka planda uygular.


11. Diğer Önemli İyileştirmeler

Daha İyi Hata Raporlama

React 19, hydration hatalarını çok daha anlaşılır bir formatta raporluyor. Artık birden fazla hata yerine, sorunu ve çözümü net biçimde gösteren tek bir mesaj göreceksiniz.

Kaynak Ön Yükleme API'leri

React DOM, kaynak ön yükleme işlemleri için yeni API'ler sunuyor:

import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';

function AppHeader() {
  // DNS ön çözümlemesi
  prefetchDNS('https://api.example.com');

  // Bağlantı ön hazırlığı
  preconnect('https://cdn.example.com');

  // Kritik kaynakları ön yükle
  preload('/fonts/inter.woff2', { as: 'font', type: 'font/woff2' });

  // Script'i ön başlat
  preinit('/scripts/analytics.js', { as: 'script' });

  return <header>{/* ... */}</header>;
}

Server Components Desteği

React 19, Server Components'ı resmi olarak kararlı hale getiriyor. Server Components, bileşenlerinizi sunucu tarafında çalıştırarak bundle boyutunu küçültmenize ve veritabanı gibi kaynaklara doğrudan erişmenize olanak tanır:

// Bu bileşen SADECE sunucuda çalışır
async function RecentPosts() {
  // Doğrudan veritabanına erişim - API endpoint'e gerek yok!
  const posts = await db.query('SELECT * FROM posts ORDER BY created_at DESC LIMIT 10');

  return (
    <section>
      <h2>Son Yazılar</h2>
      {posts.map((post) => (
        <article key={post.id}>
          <h3>{post.title}</h3>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </section>
  );
}

Geçiş Rehberi: React 18'den React 19'a

React 19'a geçiş yapmak için aşağıdaki adımları izleyin:

# React 19'u yükleyin
npm install react@19 react-dom@19

# TypeScript kullanıyorsanız type tanımlarını güncelleyin
npm install @types/react@19 @types/react-dom@19

Dikkat edilmesi gereken kırılma değişiklikleri:


Sonuç

React 19, geliştiricilerin uzun süredir beklediği birçok soruna zarif çözümler sunuyor. Actions ile form yönetimi dramatik şekilde basitleşirken, use() hook ile veri çekme kalıpları çok daha sezgisel hale geliyor. useOptimistic ile iyimser güncellemeler artık birkaç satır kodla halledilebilir durumda. React Compiler ise manuel performans optimizasyonlarından bizi tamamen kurtarma vaadinde bulunuyor.

Özellikle dikkat çekici olan nokta, React ekibinin geliştirici deneyimini (DX) ön plana koyarak boilerplate kodu minimuma indirme hedefidir. Metadata yönetiminden ref kullanımına, stylesheet önceliklendirmesinden hata raporlamaya kadar her alanda "daha az kod, daha fazla iş" felsefesi hâkim.

Eğer yeni bir proje başlıyorsanız React 19 ile başlamak mantıklı bir tercih. Mevcut projeleriniz için ise resmi geçiş rehberini takip ederek kademeli bir güncelleme stratejisi izlemenizi öneririm. React 19, modern web geliştirmenin geleceğine güçlü bir adım atıyor ve bu treni kaçırmamak büyük avantaj sağlayacaktır.


Share this post on:

Önceki Yazı
React Server Components: Derinlemesine Teknik İnceleme ve Pratik Rehber