React: useActionState e useFormStatus
![](/_vercel/image?url=_astro%2Freact-19_8.Ck7_H-D3.jpg&w=984&q=100)
React 19 introduce diverse nuove funzionalità interessanti, tra cui l’hook useActionState. Questo hook sperimentale mira a semplificare la gestione dello stato e delle azioni nei componenti React, in particolare per i moduli. In questo articolo, esploreremo useActionState
confrontandolo con l’hook ben noto useState
, con alcuni esempi di codice per entrambi e discutendo i loro pro e contro.
Confronto tra useActionState e useState
Consideriamo un semplice modulo di login con campi per nome utente e password.
Esempio con useState
:
import React, { useState } from 'react';
function App() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [response, setResponse] = useState(null);
const [isPending, setIsPending] = useState(false);
const [errors, setErrors] = useState(null);
const handleSubmit = async (event) => {
event.preventDefault();
// Imposto gli stati prima di inviare la richiesta
setResponse(null);
setIsPending(true);
setErrors(null);
// Invio i dati del modulo al server, simulo un ritardo di risposta
await new Promise((resolve) => setTimeout(resolve, 2000));
// Imposto gli stati dopo la risposta in base a cosa ricevo
// ovviamente in caso di risposte vere sarebbe da controllare eventuali errori
setResponse('done!');
setIsPending(false);
setErrors(null);
};
return (
<div>
<form onSubmit={handleSubmit}>
<label>Nome utente:</label>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<label>Password:</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">Accedi</button>
</form>
<div>Risposta: {response}</div>
<div>Stato: {isPending ? 'caricamento...' : 'pronto'}</div>
</div>
);
}
export default App
Esempio con useActionState:
import React, { useActionState } from 'react';
function App() {
const sendRequest = async (prevState, formData) => {
const username = formData.get("username");
const password = formData.get("password");
// simulo l'invio di dati al server con un ritardo
// qua potrei usare una fetch request che invia username e password
await new Promise((resolve) => setTimeout(resolve, 2000));
return {data: 'done!', error: null}
// in caso di errore invece avrei avuto qualcosa di simile
// return {data: prevState, error: 'Errore!'}
}
const [userData, submitAction, isPending] = useActionState(sendRequest, {
data: null,
error: null,
});
return (
<div>
<form action={submitAction}>
<label>Nome utente:</label>
<input type="text" name="username" />
<label>Password:</label>
<input type="password" name="password" />
<button type="submit">Accedi</button>
</form>
<div>Risposta: {userData.data}</div>
<div>Stato: {isPending ? 'caricamento...' : 'pronto'}</div>
</div>
);
}
export default App
Cosa cambia tra la versione con useState
e quella con useActionState
Vediamo come cambia il nostro codice così da comprendere poi le migliorie.
- Non abbiamo più la necessità di gestire gli stati dei vari campi di input, l’importante è dare a tutti il corretto
name
. Via quindivalue
eonChange
=> form più pulito; - Non dobbiamo più gestire gli stati del form, questi sono già gestiti dal nuovo hook e vedremo che abbiamo a disposizione anche ulteriori vantaggi con l’uso di
useFormStatus
; - Allo stesso modo vengono esposti gli errori che l’invio dei dati eventualmente genera.
Il nuovo hook useFormStatus
Abbiamo anche un ulteriore vantaggio. Possiamo usare l’hook useFormStatus
per intercettare lo stato del form e ad esempio modificare il comportamento di bottoni o altri elementi del nostro form.
import { useFormStatus } from 'react-dom'; // attenzione, da `react-dom` non da `react`
const SubmitButton = () => {
const { pending } = useFormStatus();
return <button type="submit">{pending ? 'Invio in corso...' : 'Invia'}</button>
}
Questo hook lo possiamo utilizzare in qualsiasi elemento figlio del form, a qualsiasi profondità!
Modifichiamo il codice per includere anche questo componente.
import React, { useActionState } from 'react';
import { useFormStatus } from 'react-dom';
const SubmitButton = () => {
const { pending } = useFormStatus();
return <button type="submit">{pending ? 'Invio in corso...' : 'Invia'}</button>
}
function App() {
const sendRequest = async (prevState, formData) => {
const username = formData.get("username");
const password = formData.get("password");
// simulo l'invio di dati al server con un ritardo
// qua potrei usare una fetch request che invia username e password
await new Promise((resolve) => setTimeout(resolve, 2000));
return {data: 'done!', error: null}
// in caso di errore invece avrei avuto qualcosa di simile
// return {data: prevState, error: 'Errore!'}
}
const [userData, submitAction, isPending] = useActionState(sendRequest, {
data: null,
error: null,
});
return (
<div>
<form action={submitAction}>
<label>Nome utente:</label>
<input
type="text"
name="username"
/>
<label>Password:</label>
<input
type="password"
name="password"
/>
<SubmitButton />
</form>
<div>Risposta: {userData.data}</div>
<div>Stato: {isPending ? 'caricamento...' : 'pronto'}</div>
</div>
);
}
export default App
Ora quando clicchiamo sul bottone “Invia”, mentre il form sta caricando i dati, avremo la scritta “Invio in corso…“. Terminato il caricamento il bottone tornerà a mostrare “Invia”. Carino vero? Questo hook inoltre espone anche altri dati che possiamo utilizzare, sempre a qualsiasi livello!
const { pending, data, method, action } = useFormStatus();
Come vedete abbiamo a disposizione tutte le informazioni che possono esserci utili per gestire al meglio il nostro form. E ci permette di comporre i nostri componenti in modo più pulito e indipendente, senza troppe props da propagare.
Conclusione
In entrambi gli esempi, lo stato del modulo viene gestito. Tuttavia, useActionState
offre diversi vantaggi:
- Integrazione di stato e azioni:
useActionState
combina lo stato del modulo e le azioni correlate in un’unica struttura, semplificando la gestione dello stato e riducendo il codice boilerplate; - Gestione automatica dell’invio del modulo:
useActionState
fornisce un’azione submit integrata che gestisce automaticamente l’invio del modulo e l’aggiornamento dello stato in base alla risposta del server; - Riduzione del codice: L’esempio con
useActionState
è più conciso e richiede meno codice per la gestione dello stato e delle azioni del modulo;
L’hook useActionState in React 19 rappresenta un passo avanti significativo nella gestione dei moduli, offrendo un’interfaccia più semplice e coesa per lo stato e le azioni. Sebbene sia ancora una funzionalità sperimentale, il suo potenziale per semplificare lo sviluppo di moduli React è evidente.
Siamo ancora in fase beta, ma se volete testarlo basta installare le versioni beta di react e di react-dom come abbiamo visto nell’introduzione.
Buon coding 😉
Riferimenti