React 19 ve form'ların yeni hali (actions)
React 19'un kararlı sürümüyle gelen form actions modelini, mevcut yaklaşımlarla kıyaslayarak değerlendiriyorum.
React 19, 2024 sonunda kararlı (stable) sürüm olarak yayımlandı. Bu sürümün en somut yeniliklerinden biri form yönetimini yeniden düşünmesi. Yıllardır React’te form yazmak, ya controlled component kalıplarıyla biriken useState çağrıları ya da harici kütüphanelere devretmek anlamına geliyordu. React 19 bu alana birinci sınıf bir çözüm getirdi.
Form actions kavramı, form gönderimini doğrudan bir fonksiyona — bir “action”a — bağlama fikrine dayanıyor. Bu yaklaşım React Server Components ile birlikte düşünüldüğünde daha da anlam kazanıyor, ama salt istemci tarafında da kullanılabiliyor.
Önceki durumu hatırlamak
React 18 ve öncesinde basit bir form şöyle yazılırdı:
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
async function handleSubmit(e) {
e.preventDefault();
setLoading(true);
setError(null);
try {
await login({ email, password });
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={e => setEmail(e.target.value)} />
<input type="password" value={password} onChange={e => setPassword(e.target.value)} />
{error && <p>{error}</p>}
<button disabled={loading}>Giriş Yap</button>
</form>
);
}
Bu kod çalışıyor. Ama her form için bu kalıbı tekrarlamak yoruyor. loading, error ve e.preventDefault() her form bileşeninde yeniden yazılıyor.
React 19 ile action yaklaşımı
React 19, useActionState hook’unu ve form’un action prop’unu birinci sınıf olarak tanıttı:
import { useActionState } from 'react';
async function loginAction(prevState, formData) {
const email = formData.get('email');
const password = formData.get('password');
try {
await login({ email, password });
return { success: true };
} catch (err) {
return { error: err.message };
}
}
function LoginForm() {
const [state, dispatch, isPending] = useActionState(loginAction, null);
return (
<form action={dispatch}>
<input name="email" type="email" />
<input name="password" type="password" />
{state?.error && <p>{state.error}</p>}
<button disabled={isPending}>Giriş Yap</button>
</form>
);
}
Birkaç şeye dikkat etmek gerekiyor. e.preventDefault() çağrısı ortadan kalktı — action prop’u bunu otomatik olarak yönetiyor. loading state’i isPending’e dönüştü. useState çağrıları azaldı. Form verisi doğrudan FormData API’si üzerinden geliyor — her alan için ayrı state tutmak gerekmiyor.
useOptimistic ile iyimser güncelleme
React 19’un bir diğer eklemesi useOptimistic hook’u. Sunucuya istek gitmeden önce arayüzü güncelleyip, istek başarısız olursa geri almak için kullanılıyor:
function TodoList({ todos }) {
const [optimisticTodos, addOptimistic] = useOptimistic(
todos,
(state, newTodo) => [...state, { ...newTodo, pending: true }]
);
async function addTodoAction(formData) {
const title = formData.get('title');
addOptimistic({ id: Date.now(), title });
await saveTodo({ title });
}
return (
<>
{optimisticTodos.map(todo => (
<li key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>
{todo.title}
</li>
))}
<form action={addTodoAction}>
<input name="title" />
<button>Ekle</button>
</form>
</>
);
}
Bu yaklaşım, liste uygulamalarında kullanıcıya anında geri bildirim verirken arka planda sunucu çağrısını tamamlıyor. Daha önce bu iyimser güncelleme (optimistic update) kalıbını elle yönetmek ciddi bir karmaşıklık taşıyordu.
Anlık doğrulama action modeline uymaz
Action modeli her form senaryosu için doğru seçim değil. Kullanıcı yazarken alanı anlık doğrulamak istediğinizde — mesela e-posta formatı için anında geri bildirim — hâlâ useState ile kontrollü bileşen gerekiyor. FormData, submit anında veriyi yakalar; yazma sırasında değil.
Benzer şekilde, bir alanın değerine bağlı olarak başka bir alanın seçeneklerini değiştiren bağımlı alanlar da action modeliyle temiz çözülmüyor. Şehir seçince ilçelerin güncellenmesi gibi senaryolarda state’i tutmak kaçınılmaz. Dolayısıyla “action modeli her şeyin yerini aldı” demek henüz erken; bu iki model birlikte var olmaya devam edecek, seçimi kullanım durumuna bırakmak gerekiyor.
Değerlendirme
React 19’un form modeli, gerçek bir sadeleştirme sunuyor. Kontrollü bileşen kalıbı tamamen ortadan kalkmıyor — her form kullanım durumu action modeliyle çözülmüyor. Anlık doğrulama (real-time validation) ya da iç içe bağımlı alanlar gibi senaryolarda hâlâ state yönetimi gerekebilir. Ama temel gönderim akışları için bu yeni model daha az gürültülü.
React Native tarafında bu API’lerin desteği henüz tam değil; mobil taraftaki uyum biraz daha zaman alacak gibi görünüyor. Web projelerinde ise yeni bir proje başlatırken bu modeli tercih etmek için geçerli nedenler var.
Bir çerçevenin form modelini standartlaştırması, ekip içinde “herkes farklı bir yol kullanıyor” sorununu da çözüyor. Bu sessiz faydayı küçümsememek gerekiyor. React Hook Form, Formik ve el yapımı useState zincirleri aynı kod tabanında yan yana varolduğunda bakım maliyeti görünmez biçimde birikir; ortak bir model bu birikimi durdurur.
Bir de şunu eklemek isterim: useActionState hook’unun ilk parametresi olan prevState, birden çok submit arasında durumu biriktirmek için kullanılabiliyor. Bir çok adımlı form akışında bu özellik işe yarıyor; önceki adımın sonucunu bir sonrakine taşımak için ek bir state yöneticisine ihtiyaç kalmıyor. React’in bunu hook’un içine gömmesi, pratik bir tasarım kararı.