Introduzione
I form sono collezioni di campi input utilizzati per raccogliere dati dall’utente. In React, gestire form richiede: estrarre i valori inseriti, gestire la submission e validare l’input. Questo articolo spiega i meccanismi per implementare queste funzionalità.
Gestione della Submission
Per default, i button all’interno di un <form> hanno type="submit", che causa il reload della pagina inviando una richiesta HTTP al server. In applicazioni React single-page, questo comportamento va prevenuto.
Prevenire il Default Behavior
Si può ascoltare l’evento onSubmit del form e chiamare event.preventDefault():
function LoginForm() { function handleSubmit(event) { event.preventDefault(); // Previene il reload della pagina
// Logica di gestione submission console.log('Form submitted'); }
return ( <form onSubmit={handleSubmit}> <input type="email" name="email" /> <input type="password" name="password" /> <button type="submit">Login</button> </form> );}Tipi di button in form
type="submit"(default): Submitta il form e causa reloadtype="button": Non submitta il form, agisce come button normaletype="reset": Resetta tutti i campi del form ai valori iniziali
Estrazione dei Valori
Ci sono tre approcci principali per estrarre i valori inseriti dall’utente: useState, refs, e FormData.
Approccio con useState
Si gestisce ogni input con state separato o un oggetto combinato:
import { useState } from 'react';
function LoginForm() { const [enteredValues, setEnteredValues] = useState({ email: '', password: '' });
function handleInputChange(identifier, value) { setEnteredValues(prevValues => ({ ...prevValues, [identifier]: value // Sintassi per proprietà dinamica })); }
function handleSubmit(event) { event.preventDefault(); console.log(enteredValues); }
return ( <form onSubmit={handleSubmit}> <input type="email" value={enteredValues.email} onChange={(e) => handleInputChange('email', e.target.value)} /> <input type="password" value={enteredValues.password} onChange={(e) => handleInputChange('password', e.target.value)} /> <button type="submit">Login</button> </form> );}La sintassi [identifier] permette di accedere dinamicamente a una proprietà dell’oggetto usando una variabile come nome della proprietà.
Approccio con Refs
I refs permettono di accedere direttamente agli elementi DOM senza gestire state:
import { useRef } from 'react';
function LoginForm() { const emailRef = useRef(); const passwordRef = useRef();
function handleSubmit(event) { event.preventDefault();
const email = emailRef.current.value; const password = passwordRef.current.value;
console.log({ email, password }); }
return ( <form onSubmit={handleSubmit}> <input type="email" ref={emailRef} /> <input type="password" ref={passwordRef} /> <button type="submit">Login</button> </form> );}I refs richiedono meno codice ma rendono più difficile il reset programmatico dei valori.
Approccio con FormData
FormData è un’API built-in del browser che estrae automaticamente tutti i valori da un form:
function SignupForm() { function handleSubmit(event) { event.preventDefault();
// Crea un oggetto FormData dal form const formData = new FormData(event.target);
// Converte FormData in oggetto JavaScript const data = Object.fromEntries(formData.entries());
console.log(data); // { email: '...', password: '...', ... } }
return ( <form onSubmit={handleSubmit}> <input type="email" name="email" /> <input type="password" name="password" /> <button type="submit">Sign up</button> </form> );}Importante: Tutti gli input devono avere l’attributo name per essere estratti da FormData.
Gestire input multipli con lo stesso name
Per checkbox o select multipli con lo stesso name, usare getAll() invece di get():
const formData = new FormData(event.target);const data = Object.fromEntries(formData.entries());
// Per checkbox multipliconst selectedValues = formData.getAll('acquisition');data.acquisition = selectedValues; // Array di valori selezionatiValidazione dell’Input
La validazione può essere eseguita in diversi momenti: on keystroke, on blur, o on submit.
Validazione on Keystroke
Si valida ad ogni modifica del valore, mostrando errori in tempo reale:
function LoginForm() { const [enteredValues, setEnteredValues] = useState({ email: '', password: '' }); const [didEdit, setDidEdit] = useState({ email: false, password: false });
const emailIsInvalid = didEdit.email && !enteredValues.email.includes('@');
function handleInputChange(identifier, value) { setEnteredValues(prev => ({ ...prev, [identifier]: value })); setDidEdit(prev => ({ ...prev, [identifier]: false })); // Reset on typing }
function handleInputBlur(identifier) { setDidEdit(prev => ({ ...prev, [identifier]: true })); }
return ( <form> <input type="email" value={enteredValues.email} onChange={(e) => handleInputChange('email', e.target.value)} onBlur={() => handleInputBlur('email')} /> {emailIsInvalid && ( <p className="error">Please enter a valid email address</p> )} </form> );}Il pattern didEdit evita di mostrare errori prima che l’utente abbia iniziato a digitare.
Validazione on Submit
Si valida solo quando il form viene submittato:
function LoginForm() { const emailRef = useRef(); const [emailIsInvalid, setEmailIsInvalid] = useState(false);
function handleSubmit(event) { event.preventDefault();
const email = emailRef.current.value; const isValid = email.includes('@');
if (!isValid) { setEmailIsInvalid(true); return; // Blocca l'esecuzione se invalido }
setEmailIsInvalid(false); // Procedi con submission }
return ( <form onSubmit={handleSubmit}> <input type="email" ref={emailRef} /> {emailIsInvalid && ( <p className="error">Please enter a valid email address</p> )} <button type="submit">Login</button> </form> );}Validazione Built-in del Browser
Il browser fornisce attributi HTML per validazione automatica:
<form onSubmit={handleSubmit}> <input type="email" name="email" required minLength={6} /> <input type="password" name="password" required minLength={6} /> <button type="submit">Sign up</button></form>Attributi comuni:
required: Campo obbligatoriominLength/maxLength: Lunghezza minima/massimatype="email": Valida formato emailpattern: Regex per validazione custom
Il browser mostra automaticamente messaggi di errore se la validazione fallisce.
Combinare Validazione Browser e Custom
Si può combinare validazione built-in con logica custom:
function SignupForm() { const [passwordsNotEqual, setPasswordsNotEqual] = useState(false);
function handleSubmit(event) { event.preventDefault();
const formData = new FormData(event.target); const data = Object.fromEntries(formData.entries());
// Validazione custom if (data.password !== data['confirm-password']) { setPasswordsNotEqual(true); return; }
setPasswordsNotEqual(false); // Procedi con submission }
return ( <form onSubmit={handleSubmit}> <input type="password" name="password" required minLength={6} /> <input type="password" name="confirm-password" required /> {passwordsNotEqual && ( <p className="error">Passwords must match</p> )} <button type="submit">Sign up</button> </form> );}Reset del Form
Per resettare un form programmaticamente:
function handleSubmit(event) { event.preventDefault();
// Con FormData/refs: usa il metodo reset del form event.target.reset();
// Con useState: resetta lo state ai valori iniziali setEnteredValues({ email: '', password: '' });}Componenti Input Riutilizzabili
Per evitare duplicazione di codice, si può creare un componente Input riutilizzabile:
function Input({ label, id, error, ...props }) { return ( <div className="control"> <label htmlFor={id}>{label}</label> <input id={id} {...props} /> {error && <p className="control-error">{error}</p>} </div> );}
// Utilizzo<Input label="Email" id="email" type="email" name="email" value={enteredValues.email} onChange={(e) => handleInputChange('email', e.target.value)} onBlur={() => handleInputBlur('email')} error={emailIsInvalid ? 'Please enter a valid email' : null}/>Il spread operator {...props} permette di passare tutte le props rimanenti direttamente all’elemento input.
Custom Hook per Input
Un custom hook può incapsulare la logica di gestione input e validazione:
import { useState } from 'react';
export function useInput(defaultValue, validateFn) { const [enteredValue, setEnteredValue] = useState(defaultValue); const [didEdit, setDidEdit] = useState(false);
const valueIsValid = validateFn(enteredValue); const hasError = didEdit && !valueIsValid;
function handleInputChange(event) { setEnteredValue(event.target.value); setDidEdit(false); // Reset on typing }
function handleInputBlur() { setDidEdit(true); }
return { value: enteredValue, handleInputChange, handleInputBlur, hasError };}Utilizzo del hook:
import { useInput } from './hooks/useInput';import { isEmail, isNotEmpty, hasMinLength } from './util/validation';
function LoginForm() { const { value: emailValue, handleInputChange: handleEmailChange, handleInputBlur: handleEmailBlur, hasError: emailHasError } = useInput('', (value) => isEmail(value) && isNotEmpty(value));
const { value: passwordValue, handleInputChange: handlePasswordChange, handleInputBlur: handlePasswordBlur, hasError: passwordHasError } = useInput('', (value) => hasMinLength(value, 6));
function handleSubmit(event) { event.preventDefault();
if (emailHasError || passwordHasError) { return; }
console.log({ email: emailValue, password: passwordValue }); }
return ( <form onSubmit={handleSubmit}> <Input label="Email" id="email" type="email" value={emailValue} onChange={handleEmailChange} onBlur={handleEmailBlur} error={emailHasError ? 'Please enter a valid email' : null} /> <Input label="Password" id="password" type="password" value={passwordValue} onChange={handlePasswordChange} onBlur={handlePasswordBlur} error={passwordHasError ? 'Password must be at least 6 characters' : null} /> <button type="submit">Login</button> </form> );}Il hook accetta una funzione di validazione come secondo parametro, rendendolo riutilizzabile per diversi tipi di input.
Riepilogo
- Submission: Usare
onSubmitsul form eevent.preventDefault()per prevenire il reload - Estrazione valori: Tre approcci: useState (two-way binding), refs (accesso diretto), FormData (estrazione automatica)
- FormData: Richiede attributo
namesu tutti gli input;Object.fromEntries(formData.entries())converte in oggetto - Validazione: On keystroke (feedback immediato), on blur (dopo interazione), on submit (solo al submit), o browser built-in (attributi HTML)
- Pattern didEdit: Evita di mostrare errori prima che l’utente interagisca con l’input
- Componenti riutilizzabili: Estrarre JSX ripetuto in componenti custom con props configurabili
- Custom hooks: Incapsulare logica di state e validazione per riutilizzabilità
- Validazione browser: Attributi
required,minLength,typeforniscono validazione automatica