React

Lo stato di un form multi-step

Un form multi-step tiene un solo oggetto di stato e mostra un passo alla volta: se ogni passo ha il suo useState, tornare indietro perde i dati già inseriti.

In sintesi

  • Lo stato vive nel contenitore dei passi, non nei singoli passi.
  • Il ritorno indietro è gratis se i valori non vengono mai smontati.
  • Il pulsante Avanti valida solo i campi del passo visibile.

Un form multi-step tiene un solo oggetto di stato per tutti i passi e ne mostra uno alla volta: se ogni passo ha il suo useState, tornare indietro perde i dati già inseriti.

Uno stato, molti passi

Lo stato del form vive nel componente che contiene i passi, non nei singoli passi. Il passo riceve i valori e una funzione di aggiornamento, e non sa quale sia il passo precedente né quale venga dopo.

const [step, setStep] = useState(0);
const [values, setValues] = useState({ name: "", email: "", plan: "base" });

const update = (field) => (event) =>
  setValues((current) => ({ ...current, [field]: event.target.value }));

return step === 0 ? (
   setStep(1)} />
) : (
   setStep(0)} />
);

Con questa forma il ritorno indietro costa zero: i valori non sono mai stati smontati, quindi il passo precedente li ritrova identici, cursore escluso.

Validare dove si avanza, non dove si digita

  • Un errore per passo: il pulsante Avanti controlla solo i campi visibili, così il messaggio riguarda ciò che l'utente sta guardando.
  • Nessuna validazione al montaggio: un passo vuoto non mostra errori appena appare, altrimenti sembra già sbagliato.
  • Riepilogo derivato: l'ultimo passo legge lo stesso oggetto e non ricostruisce i dati da zero.

Il progetto in quattro comandi

npm create vite@latest multi-step -- --template react
cd multi-step
npm install
npm run dev

Vite, licenza MIT, crea il progetto; i passi si aggiungono come componenti in src/ e lo stato resta in un solo file. Il tempo di render si è misurato nel Profiler di React DevTools, passo per passo.

Tre implementazioni da leggere: multi-step-form, multistep-form e registration-stepper. Per sfogliare per categoria parti da blocchi form.

La limitazione

Lo stato unico costa il render dell'intero form a ogni tasto, perché l'oggetto cambia identità. Su cinquanta campi conviene spezzare lo stato per passo o passare a una libreria con sottoscrizioni per campo, accettando che il riepilogo legga da più fonti.

Dove continuare

Il pattern completo è in form multi-step con shadcn; la gestione dei campi è in react-hook-form con shadcn; il confronto fra stato controllato e non controllato è in form controllati e non.

Domande frequenti

Dove va tenuto lo stato del form?

Nel componente che contiene i passi. Se ogni passo tiene il proprio stato, il valore sparisce quando il passo viene smontato.

Meglio un oggetto solo o un useState per campo?

Un oggetto solo finché i campi sono pochi, perché il riepilogo legge una fonte sola. Oltre una trentina di campi conviene spezzarlo.

Quando si valida un passo?

Quando l'utente prova ad avanzare, non mentre digita e non al montaggio. Così l'errore riguarda un'azione appena fatta.

Come si salva una bozza?

Serializzando l'oggetto di stato in sessionStorage o sul server. Lo stato React da solo sparisce al refresh.

Il passo nascosto resta nel DOM?

Dipende dal rendering condizionale: se smonti il passo, perdi il focus e lo stato locale, ma i valori condivisi restano.