REACT FORM VALİDASYON YAPMAK
Form validasyon herhangi bir uygulamanın en sık yapılan işlerinden. Kayıt formu, iletişim formu, ödeme formu, profil güncelleme; her birinde "boş bırakmayın", "geçerli e-posta girin", "şifre en az 8 karakter" gibi kontroller. React'ta validasyon yapmanın onlarca yolu var; doğru yaklaşımı seçmek hem developer experience hem kullanıcı deneyimini belirler.
Modern React form validasyonu üç katman kullanır: state management (form state nerede tutulacak), validation logic (kurallar nasıl tanımlanacak) ve UI feedback (hatalar nasıl gösterilecek). Bu katmanların altında yatan controlled component ve event mantığı React öğrenme dokümantasyonunda temelden anlatılır; React Hook Form ve Zod kombinasyonu ise bu üç katmanı temiz şekilde ayıran güncel standart.
React'te form validasyonunun iki ucu var: her şeyi elle yöneten manuel yaklaşım ve şema tabanlı modern kütüphane hattı (React Hook Form + Zod gibi). İkisinin de yeri var — küçük formda kütüphane şişirme, büyük formda manuel kod bakım borcudur; strateji seçimi formun karmaşıklığına göre yapılır.
Yaklaşımlar
1. Manuel state ile
useState hooks kullanarak temel doğrulama:
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ı.
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
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>
);
}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ı'
)
});
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.
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'
});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>
);
}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.
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.
İ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.

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
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.



