webroad.online
  1. 1Web
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Unelte
  8. 8React
  9. 9State management
  10. 10Next.js
  11. 11Formulare
  12. 12Date și backend
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Animații
  16. 16Testare
  17. 17Arhitectură
Formulare · Lecția 2 din 4

React Hook Form

useForm, register, handleSubmit, formState, Controller, useFieldArray, modurile de validare.

Actualizat

Ce este React Hook Form

React Hook Form (RHF) e cea mai folosită librărie de formulare pentru React. Un singur hook, useForm, îți dă tot ce trebuie: înregistrarea câmpurilor, validarea, erorile, starea trimiterii.

Secretul performanței: e construit pe câmpuri uncontrolled. Valorile stau în DOM, nu în state React → tastarea nu re-randează tot formularul. La 50 de câmpuri, diferența se simte.

'use client'
import { useForm } from 'react-hook-form'

type Values = { email: string; password: string }

export function LoginForm() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<Values>({ defaultValues: { email: '', password: '' } })

  const onSubmit = async (values: Values) => {
    await login(values)
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)} noValidate>
      <input type="email" {...register('email', { required: 'Emailul e obligatoriu' })} aria-invalid={!!errors.email} />
      {errors.email && <p role="alert">{errors.email.message}</p>}

      <input type="password" {...register('password', { minLength: { value: 8, message: 'Minim 8 caractere' } })} />
      {errors.password && <p role="alert">{errors.password.message}</p>}

      <button disabled={isSubmitting}>Intră</button>
    </form>
  )
}

Piesele useForm

Piesă Ce face
register('name', rules) conectează un input: returnează name, ref, onChange, onBlur
handleSubmit(fn) validează, apoi apelează fn(values) doar dacă totul e valid
formState.errors erorile per câmp: errors.email?.message
formState.isSubmitting true cât rulează onSubmit (async)
formState.isDirty / dirtyFields ce s-a modificat față de defaultValues
formState.isValid formularul e valid acum
watch('name') abonare la o valoare — re-randează când se schimbă (folosește cu măsură)
setValue, reset, setError control din cod: completare, resetare, eroare venită de la server

Când se validează — modul

useForm({ mode: 'onTouched' })
mode Validează Experiența
onSubmit (implicit) la trimitere, apoi la fiecare schimbare erori doar după primul submit
onBlur când părăsești câmpul —
onTouched după primul blur, apoi la fiecare schimbare cel mai prietenos — nu țipă cât încă scrii
onChange la fiecare tastă agresiv, costisitor

Componente care nu sunt input nativ: Controller

register merge cu <input>, <select>, <textarea>. Pentru componente custom (date picker, select dintr-o librărie UI) folosești Controller, care le transformă în câmpuri controlled:

<Controller
  control={control}
  name="country"
  render={({ field, fieldState }) => <CountrySelect {...field} error={fieldState.error?.message} />}
/>

Liste dinamice: useFieldArray

const { fields, append, remove } = useFieldArray({ control, name: 'phones' })

{fields.map((field, i) => (
  <div key={field.id}>                                   {/* field.id, nu indexul */}
    <input {...register(`phones.${i}.number`)} />
    <button type="button" onClick={() => remove(i)}>Șterge</button>
  </div>
))}
<button type="button" onClick={() => append({ number: '' })}>Adaugă telefon</button>

Validare cu o schemă

Regulile scrise în register se împrăștie prin JSX. Varianta recomandată: o schemă Zod + zodResolver — tipurile se deduc din schemă, iar aceeași schemă validează și pe server. Detalii în lecția despre Zod.

const form = useForm({ resolver: zodResolver(loginSchema), mode: 'onTouched' })

Greșeli frecvente

  • watch() fără argument în componenta principală → re-randează tot formularul la fiecare tastă. Folosește useWatch({ name }) într-o componentă mică.
  • key={index} în useFieldArray — folosește field.id.
  • Validare doar în RHF, fără validare pe server.

Pe scurt

  • useForm → register pe câmpuri, handleSubmit(onSubmit), formState.errors.
  • Uncontrolled în spate → performant; Controller pentru componente custom, useFieldArray pentru liste.
  • mode: 'onTouched' + schemă Zod cu zodResolver.

Surse oficiale

Exerciții

Ți-a fost utilă pagina?

Un click — fără cont.