Formalar — React Hook Form bilan
Muammodan boshlaylik
Yetkazib berish manzili formasi: ism, telefon, shahar, ko'cha, uy, xonadon, izoh — 7 ta maydon. useState bilan:
const [ism, setIsm] = useState('');
const [telefon, setTelefon] = useState('');
const [shahar, setShahar] = useState('');
// ... yana 4 ta
const [xatolar, setXatolar] = useState<Record<string, string>>({});
const [teginildi, setTeginildi] = useState<Record<string, boolean>>({});
Har bir maydon uchun holat, xato, "teginilganmi", validatsiya, yuborish... Va har bir harf kiritilganda butun forma qayta chiziladi. 7 maydonli formada kod 150 qatorga yetadi.
React Hook Form — formalar uchun eng ommabop kutubxona: kam kod, kam qayta chizish, validatsiya va xatolar tayyor.
O'rnatish
npm install react-hook-form
Asosiy naqsh: Controller
Vebda RHF register bilan ishlaydi. React Native da TextInput boshqacha — shuning uchun Controller:
import { useForm, Controller } from 'react-hook-form';
import { View, Text, TextInput, Pressable, StyleSheet } from 'react-native';
type ManzilForma = {
ism: string;
telefon: string;
kocha: string;
izoh: string;
};
export default function Manzil() {
const {
control,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<ManzilForma>({
defaultValues: { ism: '', telefon: '+998', kocha: '', izoh: '' },
});
async function yuborish(data: ManzilForma) {
await new Promise((r) => setTimeout(r, 1000)); // API so'rovi o'rniga
console.log('Yuborildi:', data);
}
return (
<View style={styles.ekran}>
<Controller
control={control}
name="ism"
rules={{ required: 'Ismingizni kiriting', minLength: { value: 2, message: 'Kamida 2 harf' } }}
render={({ field: { value, onChange, onBlur } }) => (
<TextInput
style={[styles.input, errors.ism && styles.xatoInput]}
placeholder="Ism"
value={value}
onChangeText={onChange}
onBlur={onBlur}
autoComplete="name"
/>
)}
/>
{errors.ism && <Text style={styles.xato}>{errors.ism.message}</Text>}
<Controller
control={control}
name="telefon"
rules={{
required: 'Telefon kerak',
pattern: { value: /^\+998\d{9}$/, message: 'Format: +998901234567' },
}}
render={({ field: { value, onChange, onBlur } }) => (
<TextInput
style={[styles.input, errors.telefon && styles.xatoInput]}
placeholder="+998901234567"
keyboardType="phone-pad"
value={value}
onChangeText={onChange}
onBlur={onBlur}
/>
)}
/>
{errors.telefon && <Text style={styles.xato}>{errors.telefon.message}</Text>}
{/* kocha, izoh — xuddi shunday */}
<Pressable
style={[styles.tugma, isSubmitting && { opacity: 0.6 }]}
onPress={handleSubmit(yuborish)}
disabled={isSubmitting}
>
<Text style={styles.tugmaMatn}>{isSubmitting ? 'Yuborilmoqda...' : 'Saqlash'}</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
ekran: { flex: 1, padding: 16, gap: 8, backgroundColor: 'white' },
input: { borderWidth: 1, borderColor: '#cbd5e1', borderRadius: 10, padding: 14, fontSize: 16 },
xatoInput: { borderColor: '#dc2626' },
xato: { color: '#dc2626', fontSize: 13, marginTop: -4 },
tugma: { backgroundColor: '#0284c7', padding: 16, borderRadius: 12, alignItems: 'center', marginTop: 8 },
tugmaMatn: { color: 'white', fontSize: 16, fontWeight: '600' },
});
| Element | Vazifasi |
|---|---|
useForm<T>({ defaultValues }) |
Forma, tiplangan |
Controller |
Maydonni formaga ulash |
rules |
Validatsiya qoidalari |
field.onChange |
onChangeText ga ulanadi |
handleSubmit(fn) |
Avval validatsiya; hammasi to'g'ri bo'lsa — fn(data) |
errors.ism?.message |
Xato matni |
isSubmitting |
fn bajarilayotganda true (async ham) |
defaultValues — majburiy odat: busiz maydonlar undefined bilan boshlanadi va "uncontrolled → controlled" ogohlantirishi chiqadi.
Takrorlanishni olib tashlash: FormInput
Har bir maydon uchun Controller + TextInput + xato — takrorlanish. Bitta komponentga:
// components/FormInput.tsx
import { Control, Controller, FieldValues, Path, RegisterOptions } from 'react-hook-form';
import { TextInput, TextInputProps, Text, View, StyleSheet } from 'react-native';
type Props<T extends FieldValues> = TextInputProps & {
control: Control<T>;
name: Path<T>;
label: string;
rules?: RegisterOptions<T, Path<T>>;
};
export function FormInput<T extends FieldValues>({ control, name, label, rules, ...props }: Props<T>) {
return (
<Controller
control={control}
name={name}
rules={rules}
render={({ field: { value, onChange, onBlur }, fieldState: { error } }) => (
<View style={{ gap: 6 }}>
<Text style={styles.label}>{label}</Text>
<TextInput
style={[styles.input, error && styles.xatoInput]}
value={value}
onChangeText={onChange}
onBlur={onBlur}
placeholderTextColor="#94a3b8"
{...props}
/>
{error ? <Text style={styles.xato}>{error.message}</Text> : null}
</View>
)}
/>
);
}
const styles = StyleSheet.create({
label: { fontSize: 14, fontWeight: '600', color: '#334155' },
input: { borderWidth: 1, borderColor: '#cbd5e1', borderRadius: 10, padding: 14, fontSize: 16 },
xatoInput: { borderColor: '#dc2626' },
xato: { color: '#dc2626', fontSize: 13 },
});
Endi forma:
<FormInput control={control} name="ism" label="Ism" rules={{ required: 'Ismingizni kiriting' }} autoComplete="name" />
<FormInput control={control} name="telefon" label="Telefon" keyboardType="phone-pad"
rules={{ required: 'Telefon kerak', pattern: { value: /^\+998\d{9}$/, message: 'Format: +998901234567' } }} />
<FormInput control={control} name="izoh" label="Izoh" multiline numberOfLines={3} />
name — TypeScript bilan tekshiriladi: name="ismm" — kompilyatsiya xatosi.
fieldState.error — har bir maydon o'z xatosini o'zi biladi.
Validatsiya qachon ishlaydi
useForm({ mode: 'onTouched' });
mode |
Qachon |
|---|---|
onSubmit (standart) |
Faqat yuborishda; keyin har o'zgarishda |
onTouched |
Maydondan chiqqanda (blur) — mobil uchun eng yaxshi |
onChange |
Har bir harfda — bezovta qiladi |
onTouched — foydalanuvchi yozishni tugatmaguncha xato ko'rsatmaydi, lekin keyingi maydonga o'tganda darhol xabar beradi.
Boshqa imkoniyatlar
const { reset, setValue, watch, setError, setFocus } = useForm<ManzilForma>();
reset(); // formani tozalash (yuborgandan keyin)
reset(serverdanKelganManzil); // tahrirlash formasi uchun qiymatlarni yuklash
setValue('kocha', 'Amir Temur 15'); // kodda qiymat berish (xaritadan tanlanganda)
const shahar = watch('shahar'); // qiymatni kuzatish — shartli maydonlar uchun
setError('telefon', { message: 'Bu raqam allaqachon ro\'yxatdan o\'tgan' }); // server xatosi
setFocus('ism'); // TextInput ref ulangan bo'lsa
Server xatosini maydonga bog'lash:
async function yuborish(data: ManzilForma) {
const javob = await api.manzilSaqlash(data);
if (javob.xatolar?.telefon) {
setError('telefon', { message: javob.xatolar.telefon });
return;
}
reset();
}
Nima uchun shunday?
Nima uchun React Hook Form har bir harfda butun formani qayta chizmaydi?
useState bilan har bir maydon qiymati komponent holatida — o'zgarsa, butun forma komponenti qayta chiziladi. RHF esa qiymatlarni o'zining ichki obyektida (ref) saqlaydi va faqat o'zgargan maydonni (Controller ni) qayta chizadi. Ota komponent esa faqat kerakli paytda — xatolar yoki isSubmitting o'zgarganda — yangilanadi. Mobil qurilmalarda, ayniqsa uzun formalarda va eski telefonlarda, bu klaviatura bilan yozishni sezilarli silliqroq qiladi.
Xulosa
Eslab qoling:
useForm<T>({ defaultValues, mode: 'onTouched' })- React Native da
Controller—field.onChange→onChangeText rules:required,minLength,pattern,validatehandleSubmit(fn)— validatsiyadan keyin yuboradi;isSubmitting— tugmani o'chirish- Qayta ishlatiladigan
FormInput<T>— tiplanganname, o'z xatosi bilan reset,setValue,watch,setError— tahrirlash, shartli maydonlar, server xatolari
Keyingi dars
22-dars: Validatsiya — Zod, xato xabarlari