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 1 din 4

Formulare în React

Controlled vs uncontrolled, FormData, useActionState, useFormStatus, erori accesibile.

Actualizat

Ce înseamnă un formular în React

Un formular are trei lucruri de gestionat: valorile câmpurilor, erorile de validare și starea trimiterii (pending, succes, eroare de server). React îți dă două moduri fundamentale de a ține valorile, iar toate librăriile de formulare sunt construite peste unul dintre ele.

Baza HTML (name, label, tipuri de input, validare nativă) e în lecția Formulare HTML — aici construim peste ea.

Controlled vs uncontrolled

Diferența dintre un input controlled și unul uncontrolled:

Controlled Uncontrolled
Unde stă valoarea în state React (useState) în DOM (input-ul o ține singur)
Cum o citești din variabilă, oricând la submit: FormData sau ref
Re-randare la fiecare tastă da nu
Cod value + onChange pe fiecare câmp doar name (+ defaultValue)
Când valoarea influențează UI-ul în timp real: căutare live, câmpuri dependente, formatare (telefon, card) majoritatea formularelor: login, contact, setări
// controlled
const [email, setEmail] = useState('')
<input value={email} onChange={e => setEmail(e.target.value)} />

// uncontrolled
<input name="email" defaultValue={user.email} />

value fără onChange = input blocat (read-only). defaultValue = valoare inițială, apoi DOM-ul preia controlul.

Citirea datelor: FormData

function onSubmit(e: React.FormEvent<HTMLFormElement>) {
  e.preventDefault()
  const form = new FormData(e.currentTarget)
  const email = String(form.get('email'))
  const tags = form.getAll('tags')            // checkbox-uri cu același name → array
  const data = Object.fromEntries(form)       // ⚠️ păstrează doar ultima valoare pentru name-uri repetate
}

Valorile din FormData sunt mereu stringuri (sau File). Un checkbox bifat trimite 'on', unul nebifat nu apare deloc.

React 19: formulare cu acțiuni

React 19 permite să dai o funcție direct în action. React se ocupă de preventDefault, de FormData și de starea pending.

'use client'
import { useActionState } from 'react'
import { useFormStatus } from 'react-dom'

type State = { error?: string; ok?: boolean }

async function subscribe(prev: State, formData: FormData): Promise<State> {
  const email = String(formData.get('email') ?? '')
  if (!email.includes('@')) return { error: 'Email invalid' }
  await api('/subscribe', { method: 'POST', body: formData })
  return { ok: true }
}

function SubmitButton() {
  const { pending } = useFormStatus()          // citește starea formularului părinte
  return <button disabled={pending}>{pending ? 'Se trimite…' : 'Abonează-mă'}</button>
}

export function Newsletter() {
  const [state, action] = useActionState(subscribe, {})
  return (
    <form action={action}>
      <input name="email" type="email" required />
      {state.error && <p role="alert">{state.error}</p>}
      <SubmitButton />
    </form>
  )
}
Hook Ce îți dă Unde
useActionState(fn, initial) [state, action, isPending] — rezultatul ultimei trimiteri componenta cu <form>
useFormStatus() { pending, data } al formularului părinte o componentă copil a formularului
useOptimistic UI actualizat înainte de răspuns liste, like-uri

Cu o Server Action în loc de funcția client, același formular merge și fără JavaScript.

Erori accesibile

O eroare trebuie să fie văzută și auzită:

<label htmlFor="email">Email</label>
<input id="email" name="email" aria-invalid={!!error} aria-describedby={error ? 'email-error' : undefined} />
{error && <p id="email-error" role="alert">{error}</p>}
Atribut Rol
aria-invalid screen reader-ul anunță „invalid”; stilizezi cu aria-invalid:border-danger
aria-describedby leagă mesajul de câmp — e citit când userul ajunge pe câmp
role="alert" / aria-live mesajul e anunțat imediat ce apare

Când îți trebuie o librărie

Formularul are... Fără librărie Cu librărie (React Hook Form / Conform)
2–5 câmpuri, validare simplă ✓ useActionState + FormData inutil
validare la blur / la tastare, mesaje per câmp greoi ✓
liste dinamice (adaugă / șterge rânduri) greoi ✓
wizard în mai mulți pași, câmpuri dependente greoi ✓

Pe scurt

  • Uncontrolled + FormData pentru majoritatea formularelor; controlled când valoarea schimbă UI-ul live.
  • React 19: action={fn}, useActionState pentru rezultat, useFormStatus pentru pending.
  • Erori: aria-invalid + aria-describedby + role="alert".

Surse oficiale

Exerciții

Ți-a fost utilă pagina?

Un click — fără cont.