REACT FORM VALİDASYON YAPMAK

React form validasyon; controlled component, React Hook Form ve Zod gibi araçlarla state management, validation logic ve UI feedback katmanlarını ayırarak kullanıcı dostu bir doğrulama akışı kurmayı sağlar. Bu yazıda manuel state'ten şema tabanlı modern yaklaşıma kadar seçenekleri kod örnekleriyle öğreneceksiniz.
1. Yaklaşımlar
1. Manuel state ile
useState hooks kullanarak temel doğrulama; controlled component ve event mantığı React öğrenme dokümantasyonunda temelden anlatılır:
import { useState } from 'react';
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState({});
const validate = () => {
const newErrors = {};
if (!email.includes('@')) {
newErrors.email = 'Geçerli e-posta girin';
}
if (password.length < 8) {
newErrors.password = 'Şifre en az 8 karakter';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validate()) {
// submit logic
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
{errors.email && <p>{errors.email}</p>}
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{errors.password && <p>{errors.password}</p>}
<button type="submit">Giriş</button>
</form>
);
}Basit formlar için yeterli; ama 5+ alanlı formlarda kod karmaşıklaşır.
2. React Hook Form (modern standart)
npm install react-hook-form
import { useForm } from 'react-hook-form';
function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="email"
{...register('email', {
required: 'E-posta gerekli',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Geçersiz e-posta'
}
})}
/>
{errors.email && <p>{errors.email.message}</p>}
<input
type="password"
{...register('password', {
required: 'Şifre gerekli',
minLength: { value: 8, message: 'En az 8 karakter' }
})}
/>
{errors.password && <p>{errors.password.message}</p>}
<button type="submit">Giriş</button>
</form>
);
}useState yok; daha temiz, daha az re-render, daha hızlı.
2. React Hook Form avantajları
- Performance: Her tuş basışında re-render olmaz; sadece submit veya error değişiminde
- Uncontrolled: ref tabanlı; daha az state yükü
- Built-in validation: required, min, max, pattern, validate
- Schema integration: Yup, Zod, Joi ile entegre
- TypeScript desteği: Mükemmel type inference
3. Zod ile schema validation
Yapılandırılmış validation için Zod en modern kütüphane:
npm install zod @hookform/resolversimport { z } from 'zod';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
const schema = z.object({
email: z.string().email('Geçersiz e-posta'),
password: z.string().min(8, 'En az 8 karakter'),
age: z.number().min(18, '18 yaşından büyük olmalı'),
acceptTerms: z.boolean().refine(val => val === true, {
message: 'Şartları kabul etmelisiniz'
})
});
type FormData = z.infer<typeof schema>;
function RegisterForm() {
const { register, handleSubmit, formState: { errors } } =
useForm<FormData>({
resolver: zodResolver(schema)
});
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<input {...register('email')} />
{errors.email && <p>{errors.email.message}</p>}
<input type="password" {...register('password')} />
{errors.password && <p>{errors.password.message}</p>}
<input
type="number"
{...register('age', { valueAsNumber: true })}
/>
{errors.age && <p>{errors.age.message}</p>}
<input type="checkbox" {...register('acceptTerms')} />
{errors.acceptTerms && <p>{errors.acceptTerms.message}</p>}
<button type="submit">Kayıt</button>
</form>
);
}4. Zod schema örnekleri
Conditional validation
const schema = z.object({
type: z.enum(['individual', 'company']),
taxId: z.string().optional()
}).refine((data) => {
if (data.type === 'company' && !data.taxId) {
return false;
}
return true;
}, {
message: 'Şirket için vergi numarası zorunlu',
path: ['taxId']
});Custom validation
const schema = z.object({
password: z.string()
.min(8, 'En az 8 karakter')
.regex(/[A-Z]/, 'En az bir büyük harf')
.regex(/[0-9]/, 'En az bir rakam')
.regex(/[^A-Za-z0-9]/, 'En az bir özel karakter'),
confirmPassword: z.string()
}).refine(data => data.password === data.confirmPassword, {
message: 'Şifreler eşleşmiyor',
path: ['confirmPassword']
});Async validation
const checkEmail = async (email: string) => {
const res = await fetch(`/api/check-email?email=${email}`);
const data = await res.json();
return data.available;
};
const schema = z.object({
email: z.string().email().refine(
async (email) => await checkEmail(email),
'Bu e-posta zaten kayıtlı'
)
});
5. UI feedback patterns
Inline error
Her alanın altında o alanın hatası. En yaygın yaklaşım.
<div>
<input {...register('email')} className={errors.email ? 'border-red' : ''} />
{errors.email && (
<p className="text-red text-sm">{errors.email.message}</p>
)}
</div>Summary error
Formun üstünde tüm hataları liste halinde göster.
{Object.keys(errors).length > 0 && (
<div className="error-summary">
<h3>Hatalar:</h3>
<ul>
{Object.entries(errors).map(([key, error]) => (
<li key={key}>{error.message}</li>
))}
</ul>
</div>
)}Toast notification
Submit sonrası genel hata için. Detay inline olarak gösterilir.
6. Validation timing
| Mode | Davranış |
|---|---|
| onSubmit (varsayılan) | Sadece submit'te validate |
| onBlur | Alan focus kaybedince validate |
| onChange | Her tuşa basışta validate |
| onTouched | İlk dokunuş + sonraki tüm değişiklikler |
| all | Hem onBlur hem onChange |
UX için onBlur veya onTouched tipik tercih. onChange çok agresif; kullanıcı yazarken hata mesajı çıkar (sinir bozucu).
const { register, formState } = useForm({
mode: 'onBlur'
});7. Async submit ile loading state
function ContactForm() {
const { register, handleSubmit, formState: { isSubmitting } } = useForm();
const onSubmit = async (data) => {
try {
await fetch('/api/contact', {
method: 'POST',
body: JSON.stringify(data)
});
alert('Mesaj gönderildi');
} catch (error) {
alert('Hata oluştu');
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
<textarea {...register('message')} />
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Gönderiliyor...' : 'Gönder'}
</button>
</form>
);
}8. Erişilebilirlik
Validation UX için erişilebilirlik önemli:
<label htmlFor="email">E-posta</label>
<input
id="email"
type="email"
aria-invalid={errors.email ? 'true' : 'false'}
aria-describedby={errors.email ? 'email-error' : undefined}
{...register('email')}
/>
{errors.email && (
<p id="email-error" role="alert">
{errors.email.message}
</p>
)}aria-invalid ve aria-describedby ekran okuyucu kullanıcıları için kritik.
9. Server-side validation
Client-side validation kullanıcı deneyimi için; ama gerçek güvenlik için server'da da validasyon zorunlu. Saldırgan client'i atlayabilir.
Schema paylaşımı
İki yerde de validation yapıldığında: client hızlı feedback verir, server gerçek koruma sağlar. Zod schema'sı paylaşılabilir; aynı schema hem React'ta hem Node.js'te çalışır.

10. Dikkat Edilecek Tuzaklar
- Tüm validasyonu client'ta yapmak. Server'da da validation şart
- onChange mode kullanmak. Çok agresif; kullanıcıyı sinirlendirir
- Hata mesajları belirsiz. "Hata" yerine "E-posta @ içermeli"
- Aria attribute'ları eklememek. Erişilebilirlik kaybı
- isSubmitting kontrol etmemek. Çoklu submit; çift kayıt
- Schema'yı her yerde tekrar yazmak. Zod schema paylaşılabilir
11. Disiplinin Yerleşmesi
React form yönetimi modern frontend'in temel becerilerinden. Disiplini pekiştirerek ilerlemek için React eğitimi programları form yönetimi ve state konularını birlikte aktarır.
Toparlarsak
React form validasyon; React Hook Form + Zod modern standart. useState bazlı manuel yaklaşım küçük formlar için yeterli; orta-büyük formlar için React Hook Form performance ve okunabilirlik avantajı sağlar. Zod schema validation type-safe ve okunabilir. Validation timing UX için kritik; onBlur veya onTouched tipik tercih. Erişilebilirlik için aria attribute'ları gerekli. Client-side validation UX için; server-side güvenlik için zorunlu.
Sıkça Sorulan Sorular
React Hook Form mu Formik mı?
2024+ standardı <strong>React Hook Form</strong>. Formik 2018-2021 popülerdi ama daha çok re-render yapar, bundle büyük, performance düşük. React Hook Form uncontrolled component yaklaşımıyla daha hızlı, daha küçük bundle, TypeScript desteği daha iyi. Yeni projeler için React Hook Form; mevcut Formik projesi olduğu gibi devam edebilir.
Zod yerine Yup kullanabilir miyim?
Evet, Yup hâlâ popüler. Yup daha basit syntax; Zod daha güçlü TypeScript inference. Yup'ta type belirtmek için manual gerek; Zod schema'sından otomatik type çıkarır. Yeni projeler Zod'a kayıyor; Yup'ı sürdüren projeler de var. Hangisi seçilirse seçilsin React Hook Form ile resolver paketi var.
Validation onChange'de mi onBlur'da mı olmalı?
UX için onBlur veya onTouched ideal. onChange çok agresif; kullanıcı 'a' yazınca 'geçersiz e-posta' uyarısı kötü deneyim. onBlur kullanıcı alanı bitirip başka yere geçince validate eder; doğal. onTouched daha akıllı: ilk dokunuştan sonra her değişiklikte kontrol; yazarken hatayı görüp düzeltme imkanı.
Server-side validation gerçekten gerekli mi?
Mutlaka. Client-side validation sadece UX için; saldırgan tarayıcı tool'larıyla atlayabilir. POSTMAN veya curl ile doğrudan API'ye istek atılabilir; client validation devre dışı. Server'da aynı kuralları tekrar uygulayın. Zod schema her iki tarafta paylaşılabilir; tek schema iki yerde çalışır.
Form state'i Redux'a koymak gerekli mi?
Hayır, neredeyse her zaman gereksiz. Form state lokal; sayfa içinde tutulur. Redux/Zustand'a koyma sebebi: form sayfalar arası persist etmesi gerekiyorsa (multi-step form) veya başka componentler form değerlerine erişiyorsa. Çoğu form için React Hook Form'un kendi state'i yeterli.
Multi-step form (sihirbaz) nasıl yapılır?
İki yaklaşım. Birincisi: tek useForm, adımları conditional render; advance/back butonlarıyla aktif step değişir. Form state korunur. İkincisi: her step kendi formu, ama global state'te (Zustand) ortak veri tutulur. İkinci yaklaşım daha esnek ama karmaşık. Çoğu uygulama için tek useForm + step state yeterli.
Submit sonrası başarı/başarısızlık nasıl gösterilir?
isSubmitting ile loading state; isSubmitSuccessful ile başarı; submitCount ile total attempt. Genel pattern: try/catch içinde fetch; başarılı ise toast notification + form reset; hata ise hata mesajı. Modal veya inline başarı mesajı kullanıcıya net feedback verir. Form yeniden submit edilmemeli; kullanıcı yönlendirilir veya form sıfırlanır.


