React v19
5 dicembre 2024 del React Team
React v19 è ora disponibile su npm!
Nella nostra Guida all’upgrade a React 19, abbiamo condiviso istruzioni passo passo per aggiornare la tua app a React 19. In questo post presentiamo una panoramica delle nuove funzionalità di React 19 e di come adottarle.
Per l’elenco delle breaking change, consulta la Guida all’upgrade.
Novità in React 19
Actions
Un caso d’uso comune nelle app React è eseguire una mutazione dati e poi aggiornare lo state in risposta. Ad esempio, quando un utente invia un form per cambiare il nome, farai una richiesta API e poi gestirai la risposta. In passato, avresti dovuto gestire manualmente state pending, errori, aggiornamenti ottimistici e richieste sequenziali.
Ad esempio, potevi gestire lo state pending e di errore in useState:
// Before Actions
function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async () => {
setIsPending(true);
const error = await updateName(name);
setIsPending(false);
if (error) {
setError(error);
return;
}
redirect("/path");
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}In React 19 aggiungiamo supporto per usare funzioni async nelle transition per gestire automaticamente state pending, errori, form e aggiornamenti ottimistici.
Ad esempio, puoi usare useTransition per gestire lo state pending per te:
// Using pending state from Actions
function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name);
if (error) {
setError(error);
return;
}
redirect("/path");
})
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}La transition async imposterà immediatamente lo state isPending a true, eseguirà le richieste async e imposterà isPending a false dopo le transition. Questo ti permette di mantenere l’UI corrente reattiva e interattiva mentre i dati cambiano.
Basandosi sulle Actions, React 19 introduce useOptimistic per gestire aggiornamenti ottimistici e un nuovo hook React.useActionState per gestire i casi comuni delle Actions. In react-dom aggiungiamo <form> Actions per gestire i form automaticamente e useFormStatus per supportare i casi comuni delle Actions nei form.
In React 19, l’esempio sopra può essere semplificato in:
// Using <form> Actions and useActionState
function ChangeName({ name, setName }) {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get("name"));
if (error) {
return error;
}
redirect("/path");
return null;
},
null,
);
return (
<form action={submitAction}>
<input type="text" name="name" />
<button type="submit" disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
);
}Nella sezione successiva, analizziamo ciascuna delle nuove funzionalità Action in React 19.
Nuovo hook: useActionState
Per rendere più semplici i casi comuni per le Actions, abbiamo aggiunto un nuovo hook chiamato useActionState:
const [error, submitAction, isPending] = useActionState(
async (previousState, newName) => {
const error = await updateName(newName);
if (error) {
// You can return any result of the action.
// Here, we return only the error.
return error;
}
// handle success
return null;
},
null,
);useActionState accetta una funzione (l‘“Action”) e restituisce un’Action wrappata da chiamare. Funziona perché le Actions compongono. Quando l’Action wrappata viene chiamata, useActionState restituirà l’ultimo risultato dell’Action come data e lo state pending dell’Action come pending.
Per maggiori informazioni, consulta la documentazione di useActionState.
React DOM: Actions <form>
Le Actions sono anche integrate con le nuove funzionalità <form> di React 19 per react-dom. Abbiamo aggiunto supporto per passare funzioni come props action e formAction di elementi <form>, <input> e <button> per inviare automaticamente form con le Actions:
<form action={actionFunction}>Quando un’Action <form> ha successo, React resetterà automaticamente il form per componenti uncontrolled. Se devi resettare il <form> manualmente, puoi chiamare la nuova API React DOM requestFormReset.
Per maggiori informazioni, consulta la documentazione react-dom per <form>, <input> e <button>.
React DOM: nuovo hook useFormStatus
Nei design system, è comune scrivere componenti di design che necessitano accesso a informazioni sul <form> in cui si trovano, senza fare prop drilling fino al componente. Questo può essere fatto via Context, ma per rendere più semplice il caso comune, abbiamo aggiunto un nuovo hook useFormStatus:
import {useFormStatus} from 'react-dom';
function DesignButton() {
const {pending} = useFormStatus();
return <button type="submit" disabled={pending} />
}useFormStatus legge lo status del <form> genitore come se il form fosse un Context provider.
Per maggiori informazioni, consulta la documentazione react-dom per useFormStatus.
Nuovo hook: useOptimistic
Un altro pattern UI comune quando si esegue una mutazione dati è mostrare lo state finale in modo ottimistico mentre la richiesta async è in corso. In React 19 aggiungiamo un nuovo hook chiamato useOptimistic per renderlo più semplice:
function ChangeName({currentName, onUpdateName}) {
const [optimisticName, setOptimisticName] = useOptimistic(currentName);
const submitAction = async formData => {
const newName = formData.get("name");
setOptimisticName(newName);
const updatedName = await updateName(newName);
onUpdateName(updatedName);
};
return (
<form action={submitAction}>
<p>Your name is: {optimisticName}</p>
<p>
<label>Change Name:</label>
<input
type="text"
name="name"
disabled={currentName !== optimisticName}
/>
</p>
</form>
);
}L’hook useOptimistic renderizzerà immediatamente optimisticName mentre la richiesta updateName è in corso. Quando l’aggiornamento termina o va in errore, React tornerà automaticamente al valore currentName.
Per maggiori informazioni, consulta la documentazione di useOptimistic.
Nuova API: use
In React 19 introduciamo una nuova API per leggere risorse in render: use.
Ad esempio, puoi leggere una promise con use, e React sospenderà finché la promise non si risolve:
import {use} from 'react';
function Comments({commentsPromise}) {
// `use` will suspend until the promise resolves.
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}
function Page({commentsPromise}) {
// When `use` suspends in Comments,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
)
}Puoi anche leggere context con use, permettendoti di leggere Context condizionalmente, ad esempio dopo early return:
import {use} from 'react';
import ThemeContext from './ThemeContext'
function Heading({children}) {
if (children == null) {
return null;
}
// This would not work with useContext
// because of the early return.
const theme = use(ThemeContext);
return (
<h1 style={{color: theme.color}}>
{children}
</h1>
);
}L’API use può essere chiamata solo in render, simile agli hooks. A differenza degli hooks, use può essere chiamato condizionalmente. In futuro prevediamo di supportare più modi per consumare risorse in render con use.
Per maggiori informazioni, consulta la documentazione di use.
Nuove API statiche React DOM
Abbiamo aggiunto due nuove API a react-dom/static per la generazione di siti statici:
Queste nuove API migliorano renderToString aspettando il caricamento dei dati per la generazione di HTML statico. Sono progettate per funzionare con ambienti streaming come Node.js Streams e Web Streams. Ad esempio, in un ambiente Web Stream, puoi pre-renderizzare un albero React in HTML statico con prerender:
import { prerender } from 'react-dom/static';
async function handler(request) {
const {prelude} = await prerender(<App />, {
bootstrapScripts: ['/main.js']
});
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
});
}Le API Prerender aspetteranno che tutti i dati siano caricati prima di restituire lo stream HTML statico. Gli stream possono essere convertiti in stringhe o inviati con una risposta streaming. Non supportano lo streaming del contenuto mentre si carica, supportato dalle esistenti API di server rendering React DOM.
Per maggiori informazioni, consulta API statiche React DOM.
React Server Components
Server Components
I Server Components sono una nuova opzione che permette di renderizzare componenti in anticipo, prima del bundling, in un ambiente separato dall’applicazione client o dal server SSR. Questo ambiente separato è il “server” nei React Server Components. I Server Components possono girare una volta al build time sul server CI, o possono essere eseguiti per ogni richiesta usando un web server.
React 19 include tutte le funzionalità React Server Components incluse dal canale Canary. Questo significa che le librerie che distribuiscono Server Components possono ora puntare a React 19 come peer dependency con una export condition react-server per l’uso in framework che supportano la Full-stack React Architecture.
Per approfondire, consulta la documentazione dei React Server Components.
Server Actions
Le Server Actions permettono ai Client Components di chiamare funzioni async eseguite sul server.
Quando una Server Action è definita con la direttiva "use server", il tuo framework creerà automaticamente un riferimento alla funzione server e passerà quel riferimento al Client Component. Quando quella funzione viene chiamata sul client, React invierà una richiesta al server per eseguire la funzione e restituirà il risultato.
Le Server Actions possono essere create in Server Components e passate come props a Client Components, oppure possono essere importate e usate in Client Components.
Per approfondire, consulta la documentazione delle React Server Actions.
Miglioramenti in React 19
ref come prop
A partire da React 19, puoi accedere a ref come prop per componenti funzione:
function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
//...
<MyInput ref={ref} />I nuovi componenti funzione non avranno più bisogno di forwardRef, e pubblicheremo un codemod per aggiornare automaticamente i componenti per usare la nuova prop ref. Nelle versioni future deprecheremo e rimuoveremo forwardRef.
Diff per errori di hydration
Abbiamo anche migliorato la segnalazione errori per errori di hydration in react-dom. Ad esempio, invece di registrare più errori in DEV senza informazioni sul mismatch:
Ora registriamo un singolo messaggio con un diff del mismatch:
if (typeof window !== 'undefined').
- Variable input such as Date.now() or Math.random() which changes each time it’s called.
- Date formatting in a user’s locale which doesn’t match the server.
- External changing data without sending a snapshot of it along with the HTML.
- Invalid HTML tag nesting.
It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
https://react.dev/link/hydration-mismatch
<App>
<span>
+ Client
- Server
at throwOnHydrationMismatch
…<Context> come provider
In React 19, puoi renderizzare <Context> come provider invece di <Context.Provider>:
const ThemeContext = createContext('');
function App({children}) {
return (
<ThemeContext value="dark">
{children}
</ThemeContext>
);
}I nuovi Context provider possono usare <Context> e pubblicheremo un codemod per convertire i provider esistenti. Nelle versioni future depreceremo <Context.Provider>.
Cleanup function per le ref
Ora supportiamo il ritorno di una cleanup function dalle ref callback:
<input
ref={(ref) => {
// ref created
// NEW: return a cleanup function to reset
// the ref when element is removed from DOM.
return () => {
// ref cleanup
};
}}
/>Quando il componente viene smontato, React chiamerà la cleanup function restituita dalla ref callback. Funziona per DOM ref, ref a componenti classe e useImperativeHandle.
A causa dell’introduzione delle ref cleanup function, restituire qualsiasi altra cosa da una ref callback verrà ora rifiutato da TypeScript. La correzione di solito consiste nel smettere di usare return impliciti, ad esempio:
- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />Il codice originale restituiva l’istanza dell’HTMLDivElement e TypeScript non sapeva se doveva essere una cleanup function o se non volevi restituire una cleanup function.
Puoi fare codemod di questo pattern con no-implicit-ref-callback-return.
Valore iniziale di useDeferredValue
Abbiamo aggiunto un’opzione initialValue a useDeferredValue:
function Search({deferredValue}) {
// On initial render the value is ''.
// Then a re-render is scheduled with the deferredValue.
const value = useDeferredValue(deferredValue, '');
return (
<Results query={value} />
);
}Quando initialValue è fornito, useDeferredValue lo restituirà come value per il render iniziale del componente e pianificherà una ri-renderizzazione in background con il deferredValue restituito.
Per approfondire, consulta useDeferredValue.
Supporto per Document Metadata
In HTML, i tag di metadata del documento come <title>, <link> e <meta> sono riservati al posizionamento nella sezione <head> del documento. In React, il componente che decide quale metadata è appropriato per l’app può essere molto lontano dal punto in cui renderizzi il <head> o React non renderizza affatto il <head>. In passato, questi elementi dovevano essere inseriti manualmente in un effetto o da librerie come react-helmet, e richiedevano gestione attenta durante il server rendering di un’applicazione React.
In React 19 aggiungiamo supporto per renderizzare tag di metadata del documento nei componenti nativamente:
function BlogPost({post}) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title>
<meta name="author" content="Josh" />
<link rel="author" href="https://twitter.com/joshcstory/" />
<meta name="keywords" content={post.keywords} />
<p>
Eee equals em-see-squared...
</p>
</article>
);
}Quando React renderizza questo componente, vedrà i tag <title>, <link> e <meta> e li solleverà automaticamente nella sezione <head> del documento. Supportando nativamente questi tag di metadata, possiamo assicurarci che funzionino con app solo client, streaming SSR e Server Components.
Per maggiori informazioni, consulta la documentazione di <title>, <link>, and <meta>.
Supporto per stylesheet
Gli stylesheet, sia collegati esternamente (<link rel="stylesheet" href="...">) sia inline (<style>...</style>), richiedono posizionamento attento nel DOM a causa delle regole di precedenza degli stili. Costruire una capacità stylesheet che permetta componibilità nei componenti è difficile, quindi gli utenti spesso finiscono per caricare tutti gli stili lontano dai componenti che possono dipenderne, oppure usano una libreria di stili che incapsula questa complessità.
In React 19 affrontiamo questa complessità e forniamo un’integrazione ancora più profonda nel Concurrent Rendering sul Client e nello Streaming Rendering sul Server con supporto integrato per gli stylesheet. Se dici a React la precedence del tuo stylesheet, gestirà l’ordine di inserimento dello stylesheet nel DOM e assicurerà che lo stylesheet (se esterno) sia caricato prima di rivelare contenuto che dipende da quelle regole di stile.
function ComponentOne() {
return (
<Suspense fallback="loading...">
<link rel="stylesheet" href="foo" precedence="default" />
<link rel="stylesheet" href="bar" precedence="high" />
<article class="foo-class bar-class">
{...}
</article>
</Suspense>
)
}
function ComponentTwo() {
return (
<div>
<p>{...}</p>
<link rel="stylesheet" href="baz" precedence="default" /> <-- will be inserted between foo & bar
</div>
)
}Durante il Server Side Rendering React includerà lo stylesheet nel <head>, assicurando che il browser non faccia paint finché non è caricato. Se lo stylesheet viene scoperto tardi dopo che abbiamo già iniziato lo streaming, React assicurerà che lo stylesheet venga inserito nel <head> sul client prima di rivelare il contenuto di un boundary Suspense che dipende da quello stylesheet.
Durante il Client Side Rendering React aspetterà che i nuovi stylesheet renderizzati siano caricati prima di committare il render. Se renderizzi questo componente da più punti nella tua applicazione, React includerà lo stylesheet nel documento una sola volta:
function App() {
return <>
<ComponentOne />
...
<ComponentOne /> // won't lead to a duplicate stylesheet link in the DOM
</>
}Per gli utenti abituati a caricare stylesheet manualmente, questa è un’opportunità per posizionare quegli stylesheet accanto ai componenti che dipendono da essi, permettendo un migliore ragionamento locale e rendendo più semplice assicurarsi di caricare solo gli stylesheet di cui hai effettivamente bisogno.
Le librerie di stili e le integrazioni di stile con i bundler possono anche adottare questa nuova capacità, quindi anche se non renderizzi direttamente i tuoi stylesheet, puoi comunque beneficiarne man mano che i tuoi strumenti vengono aggiornati per usare questa funzionalità.
Per maggiori dettagli, consulta la documentazione di <link> and <style>.
Supporto per script async
In HTML, gli script normali (<script src="...">) e gli script deferred (<script defer="" src="...">) si caricano nell’ordine del documento, il che rende difficile renderizzare questi tipi di script in profondità nell’albero dei componenti. Gli script async (<script async="" src="...">) invece si caricano in ordine arbitrario.
In React 19 abbiamo incluso un supporto migliore per gli script async permettendoti di renderizzarli ovunque nell’albero dei componenti, dentro i componenti che effettivamente dipendono dallo script, senza dover gestire lo spostamento e la deduplicazione delle istanze script.
function MyComponent() {
return (
<div>
<script async={true} src="..." />
Hello World
</div>
)
}
function App() {
<html>
<body>
<MyComponent>
...
<MyComponent> // won't lead to duplicate script in the DOM
</body>
</html>
}In tutti gli ambienti di rendering, gli script async verranno deduplicati così che React caricherà ed eseguirà lo script una sola volta anche se viene renderizzato da più componenti diversi.
Nel Server Side Rendering, gli script async verranno inclusi nel <head> e prioritizzati dietro risorse più critiche che bloccano il paint come stylesheet, font e preload di immagini.
Per maggiori dettagli, consulta la documentazione di <script>.
Supporto per preload delle risorse
Durante il caricamento iniziale del documento e negli aggiornamenti client side, informare il Browser delle risorse che probabilmente dovrà caricare il prima possibile può avere un effetto drammatico sulle performance della pagina.
React 19 include diverse nuove API per caricare e pre-caricare risorse del Browser per rendere il più semplice possibile costruire grandi esperienze non limitate da un caricamento inefficiente delle risorse.
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
function MyComponent() {
preinit('https://.../path/to/some/script.js', {as: 'script' }) // loads and executes this script eagerly
preload('https://.../path/to/font.woff', { as: 'font' }) // preloads this font
preload('https://.../path/to/stylesheet.css', { as: 'style' }) // preloads this stylesheet
prefetchDNS('https://...') // when you may not actually request anything from this host
preconnect('https://...') // when you will request something but aren't sure what
}<!-- the above would result in the following DOM/HTML -->
<html>
<head>
<!-- links/scripts are prioritized by their utility to early loading, not call order -->
<link rel="prefetch-dns" href="https://...">
<link rel="preconnect" href="https://...">
<link rel="preload" as="font" href="https://.../path/to/font.woff">
<link rel="preload" as="style" href="https://.../path/to/stylesheet.css">
<script async="" src="https://.../path/to/some/script.js"></script>
</head>
<body>
...
</body>
</html>Queste API possono essere usate per ottimizzare i caricamenti iniziali della pagina spostando la scoperta di risorse aggiuntive come i font fuori dal caricamento degli stylesheet. Possono anche rendere gli aggiornamenti client più veloci prefetchando un elenco di risorse usate da una navigazione anticipata e poi pre-caricando eager quelle risorse al click o persino all’hover.
Per maggiori dettagli consulta le Resource Preloading APIs.
Compatibilità con script di terze parti ed estensioni
Abbiamo migliorato l’hydration per tenere conto di script di terze parti ed estensioni del browser.
Durante l’hydration, se un elemento renderizzato sul client non corrisponde all’elemento trovato nell’HTML dal server, React forzerà una ri-renderizzazione lato client per correggere il contenuto. In precedenza, se un elemento veniva inserito da script di terze parti o estensioni del browser, scatenava un errore di mismatch e una renderizzazione lato client.
In React 19, i tag inaspettati in <head> e <body> verranno saltati, evitando errori di mismatch. Se React deve re-renderizzare l’intero documento a causa di un hydration mismatch non correlato, lascerà in place gli stylesheet inseriti da script di terze parti ed estensioni del browser.
Migliore segnalazione errori
Abbiamo migliorato la gestione errori in React 19 per rimuovere la duplicazione e fornire opzioni per gestire errori catturati e non catturati. Ad esempio, quando c’è un errore in render catturato da un contenitore di errori, in precedenza React lanciava l’errore due volte (una per l’errore originale, poi di nuovo dopo il fallimento del recupero automatico) e poi chiamava console.error con info su dove è avvenuto l’errore.
Questo produceva tre errori per ogni errore catturato:
In React 19, registriamo un singolo errore con tutte le informazioni sull’errore incluse:
Inoltre, abbiamo aggiunto due nuove opzioni root per complementare onRecoverableError:
onCaughtError: chiamato quando React cattura un errore in un contenitore di errori.onUncaughtError: chiamato quando un errore viene lanciato e non catturato da un contenitore di errori.onRecoverableError: chiamato quando un errore viene lanciato e recuperato automaticamente.
Per maggiori informazioni ed esempi, consulta la documentazione di createRoot e hydrateRoot.
Supporto per Custom Elements
React 19 aggiunge supporto completo per custom element e supera tutti i test su Custom Elements Everywhere.
Nelle versioni passate, usare Custom Elements in React è stato difficile perché React trattava props non riconosciute come attributi piuttosto che proprietà. In React 19, abbiamo aggiunto supporto per proprietà che funziona sul client e durante SSR con la seguente strategia:
- Server Side Rendering: le props passate a un custom element verranno renderizzate come attributi se il loro tipo è un valore primitivo come
string,number, o il valore ètrue. Props con tipi non primitivi comeobject,symbol,function, o valorefalseverranno omesse. - Client Side Rendering: le props che corrispondono a una proprietà sull’istanza del Custom Element verranno assegnate come proprietà, altrimenti verranno assegnate come attributi.
Grazie a Joey Arhar per aver guidato il design e l’implementazione del supporto Custom Element in React.
Come effettuare l’upgrade
Consulta la Guida all’upgrade a React 19 per istruzioni passo passo e l’elenco completo di breaking change e modifiche rilevanti.
Nota: questo post è stato pubblicato originariamente il 25/04/2024 ed è stato aggiornato al 05/12/2024 con il rilascio stabile.