Validatsiya — Zod, xato xabarlari
Muammodan boshlaylik
Ro'yxatdan o'tish formasi. rules bilan (21-dars):
rules={{ required: '...', minLength: {...}, pattern: {...}, validate: (v) => ... }}
Muammolar:
- Qoidalar JSX ichida sochilib ketgan — formaning "shartnomasi" ni bir joyda ko'rib bo'lmaydi
- "Parol va tasdiqlash mos kelsin" kabi bir nechta maydonni bog'laydigan qoidalar noqulay
- Bir xil qoidalar backend da qayta yoziladi
- TypeScript tipi alohida, qoidalar alohida — ular bir-biridan uzoqlashib ketadi
Zod — ma'lumot sxemasini bir marta yozish: undan validatsiya ham, TypeScript tipi ham olinadi.
O'rnatish
npm install zod @hookform/resolvers
Sxema
import { z } from 'zod';
export const royxatSxema = z
.object({
ism: z.string().trim().min(2, 'Ism kamida 2 harf').max(50, 'Ism 50 harfdan oshmasin'),
telefon: z.string().regex(/^\+998\d{9}$/, 'Format: +998901234567'),
email: z.string().trim().toLowerCase().email("Email noto'g'ri"),
yosh: z.coerce.number({ message: 'Raqam kiriting' }).int().min(14, 'Kamida 14 yosh').max(120),
parol: z
.string()
.min(8, 'Kamida 8 belgi')
.regex(/[A-Z]/, 'Kamida bitta katta harf')
.regex(/\d/, 'Kamida bitta raqam'),
parolTakror: z.string(),
shartlar: z.literal(true, { message: 'Shartlarni qabul qiling' }),
})
.refine((d) => d.parol === d.parolTakror, {
message: 'Parollar mos emas',
path: ['parolTakror'],
});
export type RoyxatForma = z.infer<typeof royxatSxema>;
- Har bir maydon — tur + qoidalar + o'zbekcha xabar
.trim(),.toLowerCase()— validatsiyadan oldin tozalashz.coerce.number()—TextInputdan kelgan"25"matnini songa aylantiradi.refine— bir nechta maydonni bog'laydigan qoida;path— xato qaysi maydon tagida chiqishiz.infer— sxemadan TypeScript tipi. Tipni qo'lda yozish shart emas, va ular hech qachon farqlanmaydi
React Hook Form ga ulash
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { royxatSxema, RoyxatForma } from '@/schemas/royxat';
import { FormInput } from '@/components/FormInput';
export default function Royxat() {
const { control, handleSubmit, formState: { isSubmitting } } = useForm<RoyxatForma>({
resolver: zodResolver(royxatSxema),
mode: 'onTouched',
defaultValues: { ism: '', telefon: '+998', email: '', parol: '', parolTakror: '' },
});
async function yuborish(data: RoyxatForma) {
// data — allaqachon tozalangan va tiplangan: email kichik harfda, yosh — son
await api.royxatdanOtish(data);
}
return (
<ScrollView contentContainerStyle={{ padding: 16, gap: 14 }} keyboardShouldPersistTaps="handled">
<FormInput control={control} name="ism" label="Ism" autoComplete="name" />
<FormInput control={control} name="telefon" label="Telefon" keyboardType="phone-pad" />
<FormInput control={control} name="email" label="Email" keyboardType="email-address" autoCapitalize="none" />
<FormInput control={control} name="yosh" label="Yosh" keyboardType="number-pad" />
<FormInput control={control} name="parol" label="Parol" secureTextEntry />
<FormInput control={control} name="parolTakror" label="Parolni takrorlang" secureTextEntry />
{/* shartlar — Switch bilan Controller */}
<Tugma matn="Ro'yxatdan o'tish" onPress={handleSubmit(yuborish)} yuklanmoqda={isSubmitting} />
</ScrollView>
);
}
rules endi kerak emas — hamma qoidalar sxemada. FormInput (21-dars) o'zgarishsiz ishlaydi.
Switch va Controller
<Controller
control={control}
name="shartlar"
render={({ field: { value, onChange }, fieldState: { error } }) => (
<View>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
<Switch value={!!value} onValueChange={onChange} />
<Text>Foydalanish shartlariga roziman</Text>
</View>
{error ? <Text style={{ color: '#dc2626' }}>{error.message}</Text> : null}
</View>
)}
/>
Sxemani qayta ishlatish
// schemas/umumiy.ts
export const telefonSxema = z.string().regex(/^\+998\d{9}$/, 'Format: +998901234567');
export const emailSxema = z.string().trim().toLowerCase().email("Email noto'g'ri");
// schemas/profil.ts
export const profilSxema = z.object({
ism: z.string().trim().min(2),
telefon: telefonSxema,
email: emailSxema.optional(),
});
// Tahrirlashda — barcha maydonlar ixtiyoriy
export const profilYangilashSxema = profilSxema.partial();
API javobini tekshirish
Zod nafaqat formalar uchun. Serverdan kelgan ma'lumot ham kutilgan shaklda ekanini tekshirish mumkin:
const mahsulotSxema = z.object({
id: z.number(),
nom: z.string(),
narx: z.number().nonnegative(),
rasm: z.string().url().nullable(),
});
const javob = await fetch(url).then((r) => r.json());
const mahsulot = mahsulotSxema.parse(javob); // shakl noto'g'ri bo'lsa — aniq xato
Backend jamoasi API ni o'zgartirib yuborsa — ilova tushunarsiz undefined is not an object bilan emas, aniq "narx maydoni kutilgan edi" xatosi bilan to'xtaydi.
Agar backend Node.js / NestJS da bo'lsa — bitta sxema ham server, ham mobil ilova uchun ishlatiladi (monorepo yoki umumiy paket).
Yaxshi xato xabarlari
- Nima noto'g'ri va qanday tuzatish: "Noto'g'ri qiymat" emas, "Format: +998901234567"
- Foydalanuvchi tilida: "regex mismatch" emas
- Maydon yonida — tepadagi umumiy ro'yxatda emas
- Vaqtida: maydondan chiqqanda (
onTouched), har harfda emas - Server xatolari ham maydonga bog'lansin (
setError, 21-dars)
Nima uchun shunday?
Nima uchun frontend da validatsiya qilamiz, agar server baribir tekshiradi?
Ular turli vazifani bajaradi — xuddi PHP kursidagi kabi (20-dars). Ilovadagi validatsiya — foydalanuvchi tajribasi uchun: darhol, tarmoqsiz, aniq maydon yonida xabar. Serverdagi validatsiya — xavfsizlik va ma'lumot yaxlitligi uchun: ilovani chetlab, API ga to'g'ridan-to'g'ri so'rov yuborish mumkin. Zod bu ikkalasini bitta sxema bilan qilish imkonini beradi — qoidalar ikki joyda turlicha yozilib, bir-biridan farqlanib qolmaydi.
Xulosa
Eslab qoling:
- Zod — sxema bir marta: validatsiya + TypeScript tipi (
z.infer) .trim(),.toLowerCase(),z.coerce.number()— tozalash va aylantirish.refine(..., { path })— bog'liq maydonlar (parol tasdiqlash)zodResolver(sxema)— React Hook Form bilan;ruleskerak emas- Sxemalarni qayta ishlatish: umumiy qismlar,
.partial() sxema.parse(javob)— API javobini tekshirish- Xabarlar: aniq, o'zbekcha, maydon yonida, vaqtida
Keyingi dars
23-dars: Klaviatura — KeyboardAvoidingView, fokus, yopish