Türkçe

React'in useActionState hook'unun gücünü keşfedin. Pratik ve derinlemesine örneklerle form yönetimini nasıl basitleştirdiğini, bekleme durumlarını nasıl ele aldığını ve kullanıcı deneyimini nasıl geliştirdiğini öğrenin.

React useActionState: Modern Form Yönetimi İçin Kapsamlı Bir Rehber

Web geliştirme dünyası sürekli bir evrim içinde ve React ekosistemi bu değişimin ön saflarında yer alıyor. Son sürümlerle birlikte React, interaktif ve dayanıklı uygulamalar oluşturma şeklimizi temelden iyileştiren güçlü özellikler sundu. Bunların en etkileyicilerinden biri, formları ve asenkron işlemleri yönetmede oyunun kurallarını değiştiren useActionState hook'udur. Daha önce deneysel sürümlerde useFormState olarak bilinen bu hook, artık her modern React geliştiricisi için stabil ve temel bir araç haline geldi.

Bu kapsamlı rehber, sizi useActionState'in derinliklerine taşıyacak. Çözdüğü sorunları, temel mekaniklerini ve üstün kullanıcı deneyimleri oluşturmak için useFormStatus gibi tamamlayıcı hook'larla birlikte nasıl kullanılacağını keşfedeceğiz. İster basit bir iletişim formu, ister karmaşık, veri yoğun bir uygulama geliştiriyor olun, useActionState'i anlamak kodunuzu daha temiz, daha bildirimsel ve daha sağlam hale getirecektir.

Sorun: Geleneksel Form State Yönetiminin Karmaşıklığı

useActionState'in zarafetini takdir etmeden önce, ele aldığı zorlukları anlamalıyız. Yıllardır React'te form state'ini yönetmek, useState hook'unu kullanarak öngörülebilir ama genellikle zahmetli bir düzen içeriyordu.

Yaygın bir senaryoyu ele alalım: bir listeye yeni bir ürün eklemek için basit bir form. Birkaç state parçasını yönetmemiz gerekiyor:

Tipik bir uygulama aşağıdakine benzer olabilir:

Örnek: Birden çok useState hook'u ile 'Eski Yöntem'

// Kurgusal API fonksiyonu
const addProductAPI = async (productName) => {
await new Promise(resolve => setTimeout(resolve, 1500));
if (!productName || productName.length < 3) {
throw new Error('Product name must be at least 3 characters long.');
}
console.log(`Product "${productName}" added.`);
return { success: true };
};

// Component
import { useState } from 'react';

function OldProductForm() {
const [productName, setProductName] = useState('');
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);

const handleSubmit = async (event) => {
event.preventDefault();
setIsPending(true);
setError(null);

try {
await addProductAPI(productName);
setProductName(''); // Başarı durumunda input'u temizle
} catch (err) {
setError(err.message);
} finally {
setIsPending(false);
}
};

return (




id="productName"
name="productName"
value={productName}
onChange={(e) => setProductName(e.target.value)}
/>

{error &&

{error}

}


);
}

Bu yaklaşım işe yarar, ancak birkaç dezavantajı vardır:

  • Hazır Kod (Boilerplate): Kavramsal olarak tek bir form gönderme süreci olan şeyi yönetmek için üç ayrı useState çağrısına ihtiyacımız var.
  • Manuel State Yönetimi: Geliştirici, yüklenme ve hata durumlarını bir try...catch...finally bloğu içinde doğru sırada manuel olarak ayarlamaktan ve sıfırlamaktan sorumludur. Bu tekrarlayıcıdır ve hataya açıktır.
  • Bağımlılık (Coupling): Form gönderme sonucunu işleme mantığı, component'in render mantığıyla sıkı sıkıya bağlıdır.

useActionState ile Tanışın: Bir Paradigma Değişimi

useActionState, form gönderimi gibi asenkron bir eylemin durumunu yönetmek için özel olarak tasarlanmış bir React hook'udur. State'i doğrudan eylem fonksiyonunun sonucuna bağlayarak tüm süreci kolaylaştırır.

İmzası açık ve nettir:

const [state, formAction] = useActionState(actionFn, initialState);

Bileşenlerini inceleyelim:

  • actionFn(previousState, formData): Bu, işi yapan (örneğin, bir API'yi çağıran) asenkron fonksiyonunuzdur. Önceki state'i ve form verilerini argüman olarak alır. En önemlisi, bu fonksiyonun döndürdüğü şey yeni state olur.
  • initialState: Bu, eylem ilk kez yürütülmeden önceki state'in değeridir.
  • state: Bu, mevcut state'dir. Başlangıçta initialState'i tutar ve her yürütmeden sonra actionFn'inizin dönüş değerine güncellenir.
  • formAction: Bu, eylem fonksiyonunuzun yeni, sarmalanmış bir versiyonudur. Bu fonksiyonu <form> elemanının action prop'una geçirmelisiniz. React, eylemin bekleme durumunu izlemek için bu sarmalanmış fonksiyonu kullanır.

Pratik Örnek: useActionState ile Yeniden Düzenleme (Refactoring)

Şimdi, ürün formumuzu useActionState kullanarak yeniden düzenleyelim. İyileştirme hemen fark edilir.

İlk olarak, eylem mantığımızı uyarlamamız gerekiyor. Hata fırlatmak yerine, eylem sonucunu açıklayan bir state nesnesi döndürmelidir.

Örnek: useActionState ile 'Yeni Yöntem'

// useActionState ile çalışmak üzere tasarlanmış eylem fonksiyonu
const addProductAction = async (previousState, formData) => {
const productName = formData.get('productName');
await new Promise(resolve => setTimeout(resolve, 1500)); // Ağ gecikmesini simüle et

if (!productName || productName.length < 3) {
return { message: 'Ürün adı en az 3 karakter olmalıdır.', success: false };
}

console.log(`Product "${productName}" added.`);
// Başarı durumunda, bir başarı mesajı döndür ve formu temizle.
return { message: `"${productName}" başarıyla eklendi`, success: true };
};

// Yeniden düzenlenmiş component
import { useActionState } from 'react';
// Not: Bekleme durumunu ele almak için bir sonraki bölümde useFormStatus'u ekleyeceğiz.

function NewProductForm() {
const initialState = { message: null, success: false };
const [state, formAction] = useActionState(addProductAction, initialState);

return (





{!state.success && state.message && (

{state.message}


)}
{state.success && state.message && (

{state.message}


)}

);
}

Ne kadar daha temiz olduğuna bir bakın! Üç useState hook'unu tek bir useActionState hook'u ile değiştirdik. Component'in sorumluluğu artık tamamen `state` nesnesine dayalı olarak kullanıcı arayüzünü render etmektir. Tüm iş mantığı `addProductAction` fonksiyonu içinde düzgün bir şekilde kapsüllenmiştir. State, eylemin ne döndürdüğüne göre otomatik olarak güncellenir.

Ama bir dakika, bekleme durumu ne olacak? Form gönderilirken düğmeyi nasıl devre dışı bırakırız?

useFormStatus ile Bekleme Durumlarını Yönetme

React, tam olarak bu sorunu çözmek için tasarlanmış bir yardımcı hook olan useFormStatus'u sağlar. Son form gönderimi için durum bilgisi sağlar, ancak kritik bir kuralla: durumunu izlemek istediğiniz <form> içinde render edilen bir component'ten çağrılmalıdır.

Bu, endişelerin temiz bir şekilde ayrılmasını teşvik eder. Gönderme düğmesi gibi formun gönderim durumundan haberdar olması gereken kullanıcı arayüzü elemanları için özel bir component oluşturursunuz.

useFormStatus hook'u, en önemlisi `pending` olan birkaç özelliğe sahip bir nesne döndürür.

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

  • pending: Üst form o anda gönderiliyorsa `true`, aksi takdirde `false` olan bir boolean değer.
  • data: Gönderilmekte olan verileri içeren bir `FormData` nesnesi.
  • method: HTTP yöntemini belirten bir dize (`'get'` veya `'post'`).
  • action: Formun `action` prop'una geçirilen fonksiyona bir referans.

Duruma Duyarlı Bir Gönderme Düğmesi Oluşturma

Özel bir `SubmitButton` component'i oluşturalım ve onu formumuza entegre edelim.

Örnek: SubmitButton component'i

import { useFormStatus } from 'react-dom';
// Not: useFormStatus, 'react' yerine 'react-dom'dan import edilir.

function SubmitButton() {
const { pending } = useFormStatus();

return (

);
}

Şimdi, ana form component'imizi bunu kullanacak şekilde güncelleyebiliriz.

Örnek: useActionState ve useFormStatus ile tamamlanmış form

import { useActionState } from 'react';
import { useFormStatus } from 'react-dom';

// ... (addProductAction fonksiyonu aynı kalır)

function SubmitButton() { /* ... yukarıda tanımlandığı gibi ... */ }

function CompleteProductForm() {
const initialState = { message: null, success: false };
const [state, formAction] = useActionState(addProductAction, initialState);

return (



{/* Başarı durumunda input'u sıfırlamak için bir key ekleyebiliriz */}


{!state.success && state.message && (

{state.message}


)}
{state.success && state.message && (

{state.message}


)}

);
}

Bu yapıyla, `CompleteProductForm` component'inin bekleme durumu hakkında hiçbir şey bilmesine gerek yoktur. `SubmitButton` tamamen kendi kendine yeterlidir. Bu kompozisyonel model, karmaşık, bakımı kolay kullanıcı arayüzleri oluşturmak için inanılmaz derecede güçlüdür.

Aşamalı Geliştirmenin Gücü

Bu yeni eylem tabanlı yaklaşımın en derin faydalarından biri, özellikle Sunucu Eylemleri (Server Actions) ile kullanıldığında, otomatik aşamalı geliştirmedir (progressive enhancement). Bu, ağ koşullarının güvenilmez olabildiği ve kullanıcıların eski cihazlara sahip olabileceği veya JavaScript'i devre dışı bırakmış olabileceği küresel bir kitle için uygulamalar oluşturmada hayati bir kavramdır.

İşte nasıl çalıştığı:

  1. JavaScript Olmadan: Eğer bir kullanıcının tarayıcısı istemci tarafı JavaScript'i çalıştırmazsa, `<form action={...}>` standart bir HTML formu olarak çalışır. Sunucuya tam sayfa isteği yapar. Next.js gibi bir framework kullanıyorsanız, sunucu tarafı eylem çalışır ve framework tüm sayfayı yeni durumla (örneğin, doğrulama hatasını göstererek) yeniden render eder. Uygulama, SPA benzeri akıcılık olmadan tamamen işlevseldir.
  2. JavaScript İle: JavaScript paketi yüklendiğinde ve React sayfayı hidrate ettiğinde (hydrate), aynı `formAction` istemci tarafında yürütülür. Tam sayfa yeniden yüklemesi yerine, tipik bir fetch isteği gibi davranır. Eylem çağrılır, state güncellenir ve yalnızca component'in gerekli kısımları yeniden render edilir.

Bu, form mantığınızı bir kez yazdığınız ve her iki senaryoda da sorunsuz çalıştığı anlamına gelir. Varsayılan olarak dayanıklı, erişilebilir bir uygulama oluşturursunuz, bu da dünya genelindeki kullanıcı deneyimi için büyük bir kazançtır.

Gelişmiş Desenler ve Kullanım Durumları

1. Sunucu Eylemleri vs. İstemci Eylemleri

useActionState'e geçirdiğiniz `actionFn`, standart bir istemci tarafı asenkron fonksiyon (örneklerimizdeki gibi) veya bir Sunucu Eylemi (Server Action) olabilir. Bir Sunucu Eylemi, sunucuda tanımlanan ve istemci component'lerinden doğrudan çağrılabilen bir fonksiyondur. Next.js gibi framework'lerde, fonksiyon gövdesinin en üstüne "use server"; yönergesini ekleyerek bir tane tanımlarsınız.

  • İstemci Eylemleri (Client Actions): Yalnızca istemci tarafı state'ini etkileyen veya doğrudan istemciden üçüncü taraf API'leri çağıran mutasyonlar için idealdir.
  • Sunucu Eylemleri (Server Actions): Bir veritabanı veya diğer sunucu tarafı kaynakları içeren mutasyonlar için mükemmeldir. Her mutasyon için manuel olarak API uç noktaları oluşturma ihtiyacını ortadan kaldırarak mimarinizi basitleştirirler.

İşin güzel yanı, useActionState'in her ikisiyle de aynı şekilde çalışmasıdır. Bir istemci eylemini, component kodunu değiştirmeden bir sunucu eylemiyle değiştirebilirsiniz.

2. `useOptimistic` ile İyimser Güncellemeler (Optimistic Updates)

Daha da duyarlı bir his için, useActionState'i useOptimistic hook'u ile birleştirebilirsiniz. İyimser güncelleme, asenkron eylemin başarılı olacağını *varsayarak* kullanıcı arayüzünü anında güncellediğiniz zamandır. Eğer başarısız olursa, kullanıcı arayüzünü önceki durumuna geri döndürürsünüz.

Bir yorum eklediğiniz bir sosyal medya uygulaması hayal edin. İyimser bir şekilde, istek sunucuya gönderilirken yeni yorumu anında listede gösterirsiniz. useOptimistic, bu deseni uygulamayı basitleştirmek için eylemlerle el ele çalışmak üzere tasarlanmıştır.

3. Başarı Durumunda Formu Sıfırlama

Yaygın bir gereksinim, başarılı bir gönderimden sonra form girdilerini temizlemektir. Bunu useActionState ile başarmanın birkaç yolu vardır.

  • Key Prop Numarası: `CompleteProductForm` örneğimizde gösterildiği gibi, bir input'a veya tüm forma benzersiz bir `key` atayabilirsiniz. Key değiştiğinde, React eski component'i kaldırıp yeni bir tane monte eder, bu da state'ini etkili bir şekilde sıfırlar. Key'i bir başarı bayrağına bağlamak (`key={state.success ? 'success' : 'initial'}`) basit ve etkili bir yöntemdir.
  • Kontrollü Component'ler (Controlled Components): Gerekirse hala kontrollü component'ler kullanabilirsiniz. Input'un değerini useState ile yöneterek, useActionState'den gelen başarı durumunu dinleyen bir useEffect içinde setter fonksiyonunu çağırarak temizleyebilirsiniz.

Sık Karşılaşılan Hatalar ve En İyi Uygulamalar

  • useFormStatus'un Yerleşimi: Unutmayın, useFormStatus'u çağıran bir component, `<form>`'un bir alt öğesi olarak render edilmelidir. Eğer bir kardeş (sibling) veya ebeveyn (parent) ise çalışmaz.
  • Serileştirilebilir State: Sunucu Eylemleri kullanırken, eyleminizden dönen state nesnesi serileştirilebilir olmalıdır. Bu, fonksiyonlar, Symbol'ler veya diğer serileştirilemeyen değerleri içeremeyeceği anlamına gelir. Sade nesneler, diziler, dizeler, sayılar ve boolean'larla sınırlı kalın.
  • Eylemlerde Hata Fırlatmayın: `throw new Error()` yerine, eylem fonksiyonunuz hataları zarif bir şekilde ele almalı ve hatayı açıklayan bir state nesnesi döndürmelidir (örneğin, `{ success: false, message: 'Bir hata oluştu' }`). Bu, state'in her zaman öngörülebilir bir şekilde güncellenmesini sağlar.
  • Net Bir State Şekli Tanımlayın: Başlangıçtan itibaren state nesneniz için tutarlı bir yapı oluşturun. `{ data: T | null, message: string | null, success: boolean, errors: Record | null }` gibi bir şekil birçok kullanım durumunu kapsayabilir.

useActionState vs. useReducer: Hızlı Bir Karşılaştırma

İlk bakışta, useActionState, her ikisi de bir önceki duruma dayalı olarak state güncellemeyi içerdiği için useReducer'a benzer görünebilir. Ancak, farklı amaçlara hizmet ederler.

  • useReducer, istemci tarafında karmaşık state geçişlerini yönetmek için genel amaçlı bir hook'tur. Eylemlerin gönderilmesiyle (dispatching) tetiklenir ve birçok olası, senkron state değişikliğine sahip state mantığı için idealdir (örneğin, karmaşık, çok adımlı bir sihirbaz).
  • useActionState, tek bir, genellikle asenkron eyleme yanıt olarak değişen state için tasarlanmış özel bir hook'tur. Birincil rolü, HTML formları, Sunucu Eylemleri ve bekleme durumu geçişleri gibi React'in eş zamanlı render özellikleriyle entegre olmaktır.

Özetle: Form gönderimleri ve formlara bağlı asenkron işlemler için, useActionState modern, amaca yönelik bir araçtır. Diğer karmaşık, istemci tarafı state makineleri için, useReducer mükemmel bir seçenek olmaya devam etmektedir.

Sonuç: React Formlarının Geleceğini Kucaklamak

useActionState hook'u yeni bir API'den daha fazlasıdır; React'te formları ve veri mutasyonlarını işlemenin daha sağlam, bildirimsel ve kullanıcı merkezli bir yoluna doğru temel bir değişimi temsil eder. Onu benimseyerek şunları kazanırsınız:

  • Azaltılmış Hazır Kod: Tek bir hook, birden çok useState çağrısını ve manuel state düzenlemesini değiştirir.
  • Entegre Bekleme Durumları: Yardımcı useFormStatus hook'u ile yükleme arayüzlerini sorunsuz bir şekilde yönetin.
  • Dahili Aşamalı Geliştirme: JavaScript ile veya olmadan çalışan kod yazın, tüm kullanıcılar için erişilebilirlik ve dayanıklılık sağlayın.
  • Basitleştirilmiş Sunucu İletişimi: Sunucu Eylemleri için doğal bir uyum, tam yığın (full-stack) geliştirme deneyimini kolaylaştırır.

Yeni projelere başlarken veya mevcut olanları yeniden düzenlerken, useActionState'i kullanmayı düşünün. Sadece kodunuzu daha temiz ve daha öngörülebilir hale getirerek geliştirici deneyiminizi iyileştirmekle kalmayacak, aynı zamanda daha hızlı, daha dayanıklı ve çeşitli küresel bir kitleye erişilebilir daha yüksek kaliteli uygulamalar oluşturmanıza olanak tanıyacaktır.