Come passare a React 18
8 marzo 2022 di Rick Hanlon
Come abbiamo condiviso nel post di rilascio, React 18 introduce funzionalità alimentate dal nostro nuovo renderer concorrente, con una strategia di adozione graduale per le applicazioni esistenti. In questo post, ti guideremo attraverso i passaggi per passare a React 18.
Segnala eventuali problemi che incontri durante l’upgrade a React 18.
Installazione
Per installare l’ultima versione di React:
npm install react react-domOppure, se usi yarn:
yarn add react react-domAggiornamenti alle API di client rendering
Quando installi React 18 per la prima volta, vedrai un warning nella console:
React 18 introduce una nuova root API che offre una migliore ergonomia per la gestione delle root. La nuova root API abilita anche il nuovo renderer concorrente, che ti consente di opt-in alle funzionalità concorrenti.
// Before
import { render } from 'react-dom';
const container = document.getElementById('app');
render(<App tab="home" />, container);
// After
import { createRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(<App tab="home" />);Abbiamo anche sostituito unmountComponentAtNode con root.unmount:
// Before
unmountComponentAtNode(container);
// After
root.unmount();Abbiamo anche rimosso la callback da render, poiché di solito non produce il risultato atteso quando si usa Suspense:
// Before
const container = document.getElementById('app');
render(<App tab="home" />, container, () => {
console.log('rendered');
});
// After
function AppWithCallbackAfterRender() {
useEffect(() => {
console.log('rendered');
});
return <App tab="home" />
}
const container = document.getElementById('app');
const root = createRoot(container);
root.render(<AppWithCallbackAfterRender />);Infine, se la tua app usa server-side rendering con hydration, passa da hydrate a hydrateRoot:
// Before
import { hydrate } from 'react-dom';
const container = document.getElementById('app');
hydrate(<App tab="home" />, container);
// After
import { hydrateRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = hydrateRoot(container, <App tab="home" />);
// Unlike with createRoot, you don't need a separate root.render() call here.Per maggiori informazioni, consulta la discussione del working group qui.
Aggiornamenti alle API di server rendering
In questo rilascio, stiamo rinnovando le nostre API react-dom/server per supportare completamente Suspense sul server e lo Streaming SSR. Come parte di questi cambiamenti, stiamo deprecando la vecchia API di streaming Node, che non supporta lo streaming incrementale di Suspense sul server.
L’uso di questa API ora genera un warning:
renderToNodeStream: Deprecata ⛔️️
Invece, per lo streaming in ambienti Node, usa:
renderToPipeableStream: Nuova ✨
Stiamo anche introducendo una nuova API per supportare lo streaming SSR con Suspense per ambienti edge runtime moderni, come Deno e Cloudflare workers:
renderToReadableStream: Nuova ✨
Le seguenti API continueranno a funzionare, ma con supporto limitato per Suspense:
renderToString: Limitato ⚠️renderToStaticMarkup: Limitato ⚠️
Infine, questa API continuerà a funzionare per il rendering delle e-mail:
renderToStaticNodeStream
Per maggiori informazioni sui cambiamenti alle API di server rendering, consulta il post del working group Upgrading to React 18 on the server, un approfondimento sulla nuova Suspense SSR Architecture e il talk di Shaundai Person su Streaming Server Rendering with Suspense alla React Conf 2021.
Aggiornamenti alle definizioni TypeScript
Se il tuo progetto usa TypeScript, dovrai aggiornare le dipendenze @types/react e @types/react-dom alle ultime versioni. I nuovi tipi sono più sicuri e rilevano problemi che prima venivano ignorati dal type checker. Il cambiamento più rilevante è che la prop children ora deve essere elencata esplicitamente quando definisci le props, ad esempio:
interface MyButtonProps {
color: string;
children?: React.ReactNode;
}Consulta la pull request React 18 typings per l’elenco completo dei cambiamenti solo di tipo. Collega a esempi di correzioni nei tipi delle librerie così puoi vedere come adattare il tuo codice. Puoi usare lo script di migrazione automatizzato per portare più rapidamente il codice della tua applicazione ai nuovi tipi più sicuri.
Se trovi un bug nei tipi, apri un issue nella repo DefinitelyTyped.
Raggruppamento automatico
React 18 aggiunge miglioramenti delle prestazioni out-of-the-box facendo più raggruppamento per impostazione predefinita. Il raggruppamento è quando React raggruppa più aggiornamenti di state in una singola ri-renderizzazione per prestazioni migliori. Prima di React 18, raggruppavamo gli aggiornamenti solo all’interno dei gestori di eventi React. Gli aggiornamenti all’interno di promise, setTimeout, gestori di eventi nativi o qualsiasi altro evento non venivano raggruppati in React per impostazione predefinita:
// Before React 18 only React events were batched
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that's batching!)
}
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will render twice, once for each state update (no batching)
}, 1000);A partire da React 18 con createRoot, tutti gli aggiornamenti saranno raggruppati automaticamente, indipendentemente da dove provengono. Questo significa che gli aggiornamenti all’interno di timeout, promise, gestori di eventi nativi o qualsiasi altro evento verranno raggruppati allo stesso modo degli aggiornamenti all’interno degli eventi React:
// After React 18 updates inside of timeouts, promises,
// native event handlers or any other event are batched.
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that's batching!)
}
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that's batching!)
}, 1000);Questo è un breaking change, ma ci aspettiamo che comporti meno lavoro di renderizzazione e quindi prestazioni migliori nelle tue applicazioni. Per disattivare il raggruppamento automatico, puoi usare flushSync:
import { flushSync } from 'react-dom';
function handleClick() {
flushSync(() => {
setCounter(c => c + 1);
});
// React has updated the DOM by now
flushSync(() => {
setFlag(f => !f);
});
// React has updated the DOM by now
}Per maggiori informazioni, consulta l’approfondimento sul raggruppamento automatico.
Nuove API per le librerie
Nel React 18 Working Group abbiamo lavorato con i maintainer di librerie per creare nuove API necessarie a supportare il concurrent rendering per casi d’uso specifici in aree come gli stili e gli store esterni. Per supportare React 18, alcune librerie potrebbero dover passare a una delle seguenti API:
useSyncExternalStoreè un nuovo Hook che consente agli store esterni di supportare letture concorrenti forzando gli aggiornamenti allo store a essere sincroni. Questa nuova API è consigliata per qualsiasi libreria che si integra con state esterno a React. Per maggiori informazioni, consulta il post di panoramica useSyncExternalStore e i dettagli dell’API useSyncExternalStore.useInsertionEffectè un nuovo Hook che consente alle librerie CSS-in-JS di affrontare problemi di prestazioni legati all’iniezione di stili durante la renderizzazione. A meno che tu non abbia già costruito una libreria CSS-in-JS, non ci aspettiamo che tu la usi mai. Questo Hook viene eseguito dopo la mutazione del DOM, ma prima che gli Effetti di layout leggano il nuovo layout. Risolve un problema che esiste già in React 17 e versioni precedenti, ma è ancora più importante in React 18 perché React cede il controllo al browser durante il concurrent rendering, dandogli la possibilità di ricalcolare il layout. Per maggiori informazioni, consulta la Library Upgrade Guide for<style>.
React 18 introduce anche nuove API per il concurrent rendering come startTransition, useDeferredValue e useId, di cui parliamo di più nel post di rilascio.
Aggiornamenti a Strict Mode
In futuro, vorremmo aggiungere una funzionalità che consente a React di aggiungere e rimuovere sezioni dell’UI preservando lo state. Ad esempio, quando un utente passa a un’altra schermata e torna indietro, React dovrebbe poter mostrare immediatamente la schermata precedente. Per farlo, React smonterebbe e rimonterebbe alberi usando lo stesso component state di prima.
Questa funzionalità darà a React prestazioni migliori out-of-the-box, ma richiede che i componenti siano resilienti agli Effetti montati e distrutti più volte. La maggior parte degli Effetti funzionerà senza modifiche, ma alcuni Effetti presuppongono di essere montati o distrutti una sola volta.
Per aiutare a far emergere questi problemi, React 18 introduce un nuovo controllo solo per development in Strict Mode. Questo nuovo controllo smonterà e rimonterà automaticamente ogni componente, ogni volta che un componente viene montato per la prima volta, ripristinando lo state precedente al secondo mount.
Prima di questo cambiamento, React montava il componente e creava gli Effetti:
* React mounts the component.
* Layout effects are created.
* Effect effects are created.Con Strict Mode in React 18, React simulerà lo smontaggio e il rimontaggio del componente in modalità development:
* React mounts the component.
* Layout effects are created.
* Effect effects are created.
* React simulates unmounting the component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates mounting the component with the previous state.
* Layout effect setup code runs
* Effect setup code runsPer maggiori informazioni, consulta i post del Working Group Adding Reusable State to StrictMode e How to support Reusable State in Effects.
Configurazione dell’ambiente di test
Quando aggiorni per la prima volta i tuoi test per usare createRoot, potresti vedere questo warning nella console dei test:
Per risolvere, imposta globalThis.IS_REACT_ACT_ENVIRONMENT su true prima di eseguire il test:
// In your test setup file
globalThis.IS_REACT_ACT_ENVIRONMENT = true;Lo scopo del flag è dire a React che sta girando in un ambiente simile a un unit test. React registrerà warning utili se dimentichi di avvolgere un aggiornamento con act.
Puoi anche impostare il flag su false per dire a React che act non è necessario. Questo può essere utile per test end-to-end che simulano un ambiente browser completo.
Alla fine, ci aspettiamo che le testing library lo configurino automaticamente per te. Ad esempio, la prossima versione di React Testing Library ha supporto integrato per React 18 senza configurazione aggiuntiva.
Maggiori informazioni sull’API di test act e i cambiamenti correlati sono disponibili nel working group.
Interruzione del supporto per Internet Explorer
In questo rilascio, React interrompe il supporto per Internet Explorer, che uscirà dal supporto il 15 giugno 2022. Stiamo facendo questo cambiamento ora perché le nuove funzionalità introdotte in React 18 sono costruite usando funzionalità moderne del browser come i microtask che non possono essere adeguatamente polyfillati in IE.
Se hai bisogno di supportare Internet Explorer, ti consigliamo di restare su React 17.
Deprecazioni
react-dom:ReactDOM.renderè stata deprecata. Usarla genererà un warning e farà girare la tua app in modalità React 17.react-dom:ReactDOM.hydrateè stata deprecata. Usarla genererà un warning e farà girare la tua app in modalità React 17.react-dom:ReactDOM.unmountComponentAtNodeè stata deprecata.react-dom:ReactDOM.renderSubtreeIntoContainerè stata deprecata.react-dom/server:ReactDOMServer.renderToNodeStreamè stata deprecata.
Altri breaking change
- Timing consistente di useEffect: React ora esegue sempre il flush sincrono delle funzioni degli Effetti se l’aggiornamento è stato attivato durante un evento di input utente discreto come un click o un keydown. In precedenza, il comportamento non era sempre prevedibile o consistente.
- Errori di hydration più rigorosi: I mismatch di hydration dovuti a contenuto testuale mancante o extra sono ora trattati come errori invece che warning. React non tenterà più di “rattoppare” singoli nodi inserendo o eliminando un nodo sul client per far corrispondere il markup del server, e tornerà al client rendering fino al boundary
<Suspense>più vicino nell’albero. Questo garantisce che l’albero idratato sia consistente ed evita potenziali problemi di privacy e sicurezza causati da mismatch di hydration. - Gli alberi Suspense sono sempre consistenti: Se un componente sospende prima di essere completamente aggiunto all’albero, React non lo aggiungerà all’albero in uno state incompleto né attiverà i suoi Effetti. Invece, React scarterà completamente il nuovo albero, attenderà il completamento dell’operazione asincrona e poi riproverà a renderizzare da zero. React renderizzerà il tentativo di retry in modo concorrente e senza bloccare il browser.
- Effetto di layout con Suspense: Quando un albero si ri-sospende e torna a un fallback, React ora pulirà gli Effetti di layout e poi li ricreerà quando il contenuto all’interno del boundary viene mostrato di nuovo. Questo risolve un problema che impediva alle librerie di componenti di misurare correttamente il layout quando usate con Suspense.
- Nuovi requisiti dell’ambiente JS: React ora dipende da funzionalità moderne del browser tra cui
Promise,SymboleObject.assign. Se supporti browser e dispositivi più vecchi come Internet Explorer che non forniscono nativamente funzionalità moderne del browser o hanno implementazioni non conformi, considera di includere un polyfill globale nella tua applicazione bundled.
Altri cambiamenti rilevanti
React
- I componenti possono ora renderizzare
undefined: React non avvisa più se restituisciundefinedda un componente. Questo rende i valori di ritorno consentiti per i componenti consistenti con i valori consentiti nel mezzo di un albero di componenti. Ti consigliamo di usare un linter per prevenire errori come dimenticare un’istruzionereturnprima del JSX. - Nei test, i warning
actsono ora opt-in: Se esegui test end-to-end, i warningactnon sono necessari. Abbiamo introdotto un meccanismo opt-in così puoi abilitarli solo per i unit test dove sono utili e vantaggiosi. - Nessun warning su
setStatesu componenti smontati: In precedenza, React avvisava di memory leak quando chiamavisetStatesu un componente smontato. Questo warning era stato aggiunto per le sottoscrizioni, ma le persone lo incontravano principalmente in scenari in cui impostare lo state andava bene e le soluzioni alternative peggioravano il codice. Abbiamo rimosso questo warning. - Nessuna soppressione dei log della console: Quando usi Strict Mode, React renderizza ogni componente due volte per aiutarti a trovare effetti collaterali inattesi. In React 17, abbiamo soppresso i log della console per una delle due renderizzazioni per rendere i log più leggibili. In risposta al feedback della community che lo trovava confuso, abbiamo rimosso la soppressione. Invece, se hai React DevTools installato, le renderizzazioni del secondo log saranno visualizzate in grigio e ci sarà un’opzione (disattivata per impostazione predefinita) per sopprimerle completamente.
- Utilizzo della memoria migliorato: React ora pulisce più campi interni allo smontaggio, riducendo l’impatto di memory leak non corretti che potrebbero esistere nel codice della tua applicazione.
React DOM Server
renderToString: Non genererà più errori quando sospende sul server. Invece, emetterà l’HTML fallback per il boundary<Suspense>più vicino e poi riproverà a renderizzare lo stesso contenuto sul client. È comunque consigliato passare a un’API di streaming comerenderToPipeableStreamorenderToReadableStream.renderToStaticMarkup: Non genererà più errori quando sospende sul server. Invece, emetterà l’HTML fallback per il boundary<Suspense>più vicino.
Changelog
Puoi consultare il changelog completo qui.