Asosiy kontentga o‘tish
Innovialabs

React da formalar: React Hook Form va Zod orqali validatsiya

Amaliyot3 daq

Qisqacha

React Hook Form maydonlar va yuborishni ortiqcha qayta renderlarsiz boshqaradi, Zod sxemasi esa validatsiya qoidalarini bir marta tavsiflaydi — xuddi shu sxemadan serverda ham foydalanish mumkin.

Nega aynan shu juftlik

React Hook Form maydon qiymatlarini React holatidan tashqarida saqlaydi va har bir tugma bosilishida butun formani qayta chizmaydi. Zod ma’lumotlarni sxema bilan tavsiflaydi: tiplar, cheklovlar va xato matnlari bir joyda. Ularni @hookform/resolvers paketi bog‘laydi.

Asosiy foyda — klient va serverda bitta sxema. Qoidalar bir-biridan farq qilmaydi, formaning TypeScript tipi esa sxemadan avtomatik chiqariladi.

npm install react-hook-form zod @hookform/resolvers

1-qadam. Tushunarli xabarlarga ega sxema

Ariza formasini yig‘amiz: ism, O‘zbekiston formatidagi telefon va izoh.

// lib/lead-schema.ts
import { z } from 'zod';

const UZ_PHONE = /^\+998\d{9}$/;

export const leadSchema = z.object({
  name: z.string().trim().min(2, 'Ismingizni kiriting — kamida 2 ta belgi'),
  phone: z
    .string()
    .refine(
      (v) => UZ_PHONE.test(v.replace(/[\s()-]/g, '')),
      'Raqam formati: +998 90 123 45 67',
    ),
  message: z.string().max(1000, '1000 ta belgidan oshmasin').optional(),
});

export type Lead = z.infer<typeof leadSchema>;

Bu yerda muhim jihatlar:

  • foydalanuvchi tilidagi xabarlar ikkinchi argument sifatida beriladi;
  • telefon bo‘sh joylar, qavslar va defislar olib tashlangandan keyin tekshiriladi, shuning uchun +998 (90) 123-45-67 ham o‘tadi;
  • O‘zbekiston raqami — +998 va yana 9 ta raqam (operator kodi va abonent raqami).

2-qadam. Maydonlarni ro‘yxatdan o‘tkazish

'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { leadSchema, type Lead } from '@/lib/lead-schema';

export function LeadForm() {
  const {
    register,
    handleSubmit,
    setError,
    reset,
    formState: { errors, isSubmitting },
  } = useForm<Lead>({ resolver: zodResolver(leadSchema) });

  return (
    <form onSubmit={handleSubmit(onSubmit)} noValidate>
      <input {...register('name')} placeholder="Ism" />
      {errors.name && <p role="alert">{errors.name.message}</p>}

      <input {...register('phone')} type="tel" placeholder="+998 90 123 45 67" />
      {errors.phone && <p role="alert">{errors.phone.message}</p>}

      <textarea {...register('message')} />
      {errors.message && <p role="alert">{errors.message.message}</p>}

      {errors.root?.server && <p role="alert">{errors.root.server.message}</p>}

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Yuborilmoqda…' : 'Yuborish'}
      </button>
    </form>
  );
}

register maydonni formaga ulaydi, errors sxemadagi matnlarni saqlaydi, isSubmitting esa ikki marta yuborilmasligi uchun tugmani bloklaydi. noValidate atributi brauzerning o‘rnatilgan maslahatlarini o‘chiradi, shunda ikki xil xato ko‘rinmaydi.

3-qadam. Asinxron yuborish va server xatolari

onSubmit funksiyasini komponent ichida e’lon qiling — unga setError va reset kerak:

async function onSubmit(data: Lead) {
  const res = await fetch('/api/lead', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  });

  if (!res.ok) {
    const body = await res.json().catch(() => null);
    const fieldErrors: Record<string, string> = body?.fieldErrors ?? {};
    const entries = Object.entries(fieldErrors);
    if (entries.length) {
      for (const [field, message] of entries) {
        setError(field as keyof Lead, { message });
      }
    } else {
      setError('root.server', { message: 'Yuborib bo‘lmadi. Qaytadan urinib ko‘ring.' });
    }
    return;
  }

  reset();
}

handleSubmit onSubmit ni faqat klient validatsiyasi o‘tganda chaqiradi. Server xatolari setError orqali maydonlarga taqsimlanadi, umumiy xato esa root.server ga tushadi.

4-qadam. Serverda xuddi shu sxema

Faqat klient tekshiruviga ishonib bo‘lmaydi: so‘rovni formani chetlab o‘tib yuborish mumkin. Next.js dagi Route Handler misoli:

// app/api/lead/route.ts
import { leadSchema } from '@/lib/lead-schema';

export async function POST(req: Request) {
  const result = leadSchema.safeParse(await req.json().catch(() => null));

  if (!result.success) {
    const fieldErrors: Record<string, string> = {};
    for (const issue of result.error.issues) {
      const key = String(issue.path[0] ?? 'form');
      fieldErrors[key] ??= issue.message;
    }
    return Response.json({ fieldErrors }, { status: 400 });
  }

  // result.data tekshirilgan va tiplangan — arizani saqlaymiz
  return Response.json({ ok: true });
}

Sxema umumiy faylda turadi, shuning uchun qoidani o‘zgartirish darhol brauzerda ham, serverda ham amal qiladi.

Keng tarqalgan xatolar

  • Ma’lumotlarni faqat klientda tekshirish.
  • Har bir maydon qiymatini React Hook Form bilan parallel ravishda useState da saqlash.
  • Qoidalarni ikki marta yozish: register({ required }) da va sxemada.
  • Yuborish vaqtida tugmani bloklashni unutish.
  • O‘z matnlaringiz o‘rniga Zod ning standart inglizcha xabarlarini ko‘rsatish.

Ko‘p beriladigan savollar

Yo‘q, hammasi JavaScript da ham ishlaydi. Lekin TypeScript bilan forma tipini sxemadan bepul olasiz, maydon nomlaridagi xatolar esa yig‘ish vaqtida aniqlanadi.

Bunda yordam beramiz

O‘zingiz shug‘ullanmasdan natija olishni istasangiz — biz har kuni shu bilan shug‘ullanamiz.

Loyihani muhokama qilish

Yana o‘qing

G‘oya yoki vazifangiz bormi? Bizga aytib bering

Ish kuni davomida javob beramiz va yechim taklif qilamiz. Konsultatsiya bepul.