Guida all'upgrade a React 19
25 aprile 2024 di Ricky Hanlon
I miglioramenti aggiunti in React 19 richiedono alcune breaking change, ma abbiamo lavorato per rendere l’upgrade il più fluido possibile e non ci aspettiamo che le modifiche impattino la maggior parte delle app.
In questo post ti guidiamo nei passaggi per l’upgrade a React 19:
- Installazione
- Codemod
- Breaking change
- Nuove deprecazioni
- Modifiche rilevanti
- Modifiche TypeScript
- Changelog
Se vuoi aiutarci a testare React 19, segui i passaggi in questa guida all’upgrade e segnala eventuali problemi che incontri. Per l’elenco delle nuove funzionalità aggiunte in React 19, consulta il post di rilascio di React 19.
Installazione
Per installare l’ultima versione di React e React DOM:
npm install --save-exact react@^19.0.0 react-dom@^19.0.0Oppure, se usi Yarn:
yarn add --exact react@^19.0.0 react-dom@^19.0.0Se usi TypeScript, devi anche aggiornare i tipi.
npm install --save-exact @types/react@^19.0.0 @types/react-dom@^19.0.0Oppure, se usi Yarn:
yarn add --exact @types/react@^19.0.0 @types/react-dom@^19.0.0Includiamo anche un codemod per le sostituzioni più comuni. Vedi Modifiche TypeScript sotto.
Codemod
Per aiutare con l’upgrade, abbiamo collaborato con il team di codemod.com per pubblicare codemod che aggiorneranno automaticamente il tuo codice a molte delle nuove API e pattern in React 19.
Tutti i codemod sono disponibili nel react-codemod repo e il team Codemod ha contribuito a mantenere i codemod. Per eseguire questi codemod, consigliamo di usare il comando codemod invece di react-codemod perché è più veloce, gestisce migrazioni di codice più complesse e offre un supporto TypeScript migliore.
Le modifiche che includono un codemod includono il comando sotto.
Per l’elenco di tutti i codemod disponibili, consulta il react-codemod repo.
Breaking change
Gli errori in render non vengono rilanciati
Nelle versioni precedenti di React, gli errori lanciati durante il render venivano catturati e rilanciati. In DEV, registravamo anche in console.error, con log di errore duplicati.
In React 19, abbiamo migliorato la gestione degli errori per ridurre la duplicazione senza rilanciare:
- Errori non catturati: gli errori non catturati da un contenitore di errori vengono segnalati a
window.reportError. - Errori catturati: gli errori catturati da un contenitore di errori vengono segnalati a
console.error.
Questa modifica non dovrebbe impattare la maggior parte delle app, ma se la segnalazione errori in produzione si basa sul rilancio degli errori, potresti dover aggiornare la gestione errori. Per supportarlo, abbiamo aggiunto nuovi metodi a createRoot e hydrateRoot per la gestione errori personalizzata:
const root = createRoot(container, {
onUncaughtError: (error, errorInfo) => {
// ... log error report
},
onCaughtError: (error, errorInfo) => {
// ... log error report
}
});Per maggiori informazioni, consulta la documentazione di createRoot e hydrateRoot.
Rimosse API React deprecate
Rimosso: propTypes e defaultProps per le funzioni
PropTypes sono stati deprecati in April 2017 (v15.5.0).
In React 19 rimuoviamo i controlli propType dal pacchetto React e usarli verrà ignorato silenziosamente. Se usi propTypes, consigliamo di migrare a TypeScript o un’altra soluzione di type checking.
Rimuoviamo anche defaultProps dai componenti funzione in favore dei parametri default ES6. I componenti classe continueranno a supportare defaultProps poiché non esiste alternativa ES6.
// Before
import PropTypes from 'prop-types';
function Heading({text}) {
return <h1>{text}</h1>;
}
Heading.propTypes = {
text: PropTypes.string,
};
Heading.defaultProps = {
text: 'Hello, world!',
};// After
interface Props {
text?: string;
}
function Heading({text = 'Hello, world!'}: Props) {
return <h1>{text}</h1>;
}Rimosso: Legacy Context con contextTypes e getChildContext
Legacy Context è stato deprecato in October 2018 (v16.6.0).
Legacy Context era disponibile solo nei componenti classe usando le API contextTypes e getChildContext, ed è stato sostituito con contextType per bug sottili facili da non notare. In React 19 rimuoviamo Legacy Context per rendere React leggermente più piccolo e veloce.
Se usi ancora Legacy Context nei componenti classe, dovrai migrare alla nuova API contextType:
// Before
import PropTypes from 'prop-types';
class Parent extends React.Component {
static childContextTypes = {
foo: PropTypes.string.isRequired,
};
getChildContext() {
return { foo: 'bar' };
}
render() {
return <Child />;
}
}
class Child extends React.Component {
static contextTypes = {
foo: PropTypes.string.isRequired,
};
render() {
return <div>{this.context.foo}</div>;
}
}// After
const FooContext = React.createContext();
class Parent extends React.Component {
render() {
return (
<FooContext value='bar'>
<Child />
</FooContext>
);
}
}
class Child extends React.Component {
static contextType = FooContext;
render() {
return <div>{this.context}</div>;
}
}Rimosso: string refs
Le string refs sono state deprecate in March, 2018 (v16.3.0).
I componenti classe supportavano string refs prima di essere sostituite da ref callback per vari svantaggi. In React 19 rimuoviamo le string refs per rendere React più semplice e comprensibile.
Se usi ancora string refs nei componenti classe, dovrai migrare alle ref callback:
// Before
class MyComponent extends React.Component {
componentDidMount() {
this.refs.input.focus();
}
render() {
return <input ref='input' />;
}
}// After
class MyComponent extends React.Component {
componentDidMount() {
this.input.focus();
}
render() {
return <input ref={input => this.input = input} />;
}
}Rimosso: module pattern factories
Le module pattern factories sono state deprecate in August 2019 (v16.9.0).
Questo pattern era raramente usato e supportarlo rende React leggermente più grande e lento del necessario. In React 19 rimuoviamo il supporto per module pattern factories e dovrai migrare a funzioni regolari:
// Before
function FactoryComponent() {
return { render() { return <div />; } }
}// After
function FactoryComponent() {
return <div />;
}Rimosso: React.createFactory
createFactory è stato deprecato in February 2020 (v16.13.0).
Usare createFactory era comune prima del supporto diffuso di JSX, ma oggi è raramente usato e può essere sostituito con JSX. In React 19 rimuoviamo createFactory e dovrai migrare a JSX:
// Before
import { createFactory } from 'react';
const button = createFactory('button');// After
const button = <button />;Rimosso: react-test-renderer/shallow
In React 18 abbiamo aggiornato react-test-renderer/shallow per re-esportare react-shallow-renderer. In React 19 rimuoviamo react-test-render/shallow preferendo installare il pacchetto direttamente:
npm install react-shallow-renderer --save-dev- import ShallowRenderer from 'react-test-renderer/shallow';
+ import ShallowRenderer from 'react-shallow-renderer';Rimosse API React DOM deprecate
Rimosso: react-dom/test-utils
Abbiamo spostato act da react-dom/test-utils al pacchetto react:
ReactDOMTestUtils.act is deprecated in favor of React.act. Import act from react instead of react-dom/test-utils. See https://react.dev/warnings/react-dom-test-utils for more info.Per correggere questo warning, puoi importare act da react:
- import {act} from 'react-dom/test-utils'
+ import {act} from 'react';Tutte le altre funzioni test-utils sono state rimosse. Queste utility erano poco comuni e rendevano troppo facile dipendere da dettagli di implementazione a basso livello dei componenti e di React. In React 19, queste funzioni daranno errore quando chiamate e le loro export verranno rimosse in una versione futura.
Consulta la pagina dei warning per le alternative.
Rimosso: ReactDOM.render
ReactDOM.render è stato deprecato in March 2022 (v18.0.0). In React 19 rimuoviamo ReactDOM.render e dovrai migrare a ReactDOM.createRoot:
// Before
import {render} from 'react-dom';
render(<App />, document.getElementById('root'));
// After
import {createRoot} from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);Rimosso: ReactDOM.hydrate
ReactDOM.hydrate è stato deprecato in March 2022 (v18.0.0). In React 19 rimuoviamo ReactDOM.hydrate e dovrai migrare a ReactDOM.hydrateRoot,
// Before
import {hydrate} from 'react-dom';
hydrate(<App />, document.getElementById('root'));
// After
import {hydrateRoot} from 'react-dom/client';
hydrateRoot(document.getElementById('root'), <App />);Rimosso: unmountComponentAtNode
ReactDOM.unmountComponentAtNode è stato deprecato in March 2022 (v18.0.0). In React 19 dovrai migrare a root.unmount().
// Before
unmountComponentAtNode(document.getElementById('root'));
// After
root.unmount();Per maggiori informazioni su root.unmount() consulta createRoot e hydrateRoot.
Rimosso: ReactDOM.findDOMNode
ReactDOM.findDOMNode è stato deprecato nell’ottobre 2018 (v16.6.0).
Rimuoviamo findDOMNode perché era un legacy escape hatch lento da eseguire, fragile alla rifattorizzazione, restituiva solo il primo figlio e rompeva i livelli di astrazione (maggiori info qui). Puoi sostituire ReactDOM.findDOMNode con DOM ref:
// Before
import {findDOMNode} from 'react-dom';
function AutoselectingInput() {
useEffect(() => {
const input = findDOMNode(this);
input.select()
}, []);
return <input defaultValue="Hello" />;
}// After
function AutoselectingInput() {
const ref = useRef(null);
useEffect(() => {
ref.current.select();
}, []);
return <input ref={ref} defaultValue="Hello" />
}Nuove deprecazioni
Deprecato: element.ref
React 19 supporta ref as a prop, quindi depreciamo element.ref in favore di element.props.ref.
Accedere a element.ref genererà un warning:
Deprecato: react-test-renderer
Deprechiamo react-test-renderer perché implementa un proprio ambiente renderer che non corrisponde a quello usato dagli utenti, promuove il test dei dettagli di implementazione e si basa sull’introspezione degli internals di React.
Il test renderer è stato creato prima che fossero disponibili strategie di test più valide come React Testing Library, e ora consigliamo di usare una libreria di test moderna.
In React 19, react-test-renderer registra un warning di deprecazione ed è passato al concurrent rendering. Consigliamo di migrare i test a @testing-library/react o @testing-library/react-native per un’esperienza di test moderna e ben supportata.
Modifiche rilevanti
Modifiche a StrictMode
React 19 include diversi fix e miglioramenti a Strict Mode.
Quando si fa double rendering in Strict Mode in development, useMemo e useCallback riutilizzeranno i risultati memoizzati del primo render durante il secondo render. I componenti già compatibili con Strict Mode non dovrebbero notare differenze nel comportamento.
Come per tutti i comportamenti di Strict Mode, queste funzionalità sono progettate per far emergere proattivamente bug nei componenti durante lo development, così puoi correggerli prima del deploy in produzione. Ad esempio, durante lo development, Strict Mode invocherà due volte le ref callback al mount iniziale, per simulare cosa succede quando un componente montato viene sostituito da un fallback Suspense.
Miglioramenti a Suspense
In React 19, quando un componente sospende, React committa immediatamente il fallback del boundary Suspense più vicino senza aspettare che l’intero albero sibling venga renderizzato. Dopo il commit del fallback, React pianifica un altro render per i sibling sospesi per “pre-riscaldare” le richieste lazy nel resto dell’albero:


In precedenza, quando un componente sospendeva, i sibling sospesi venivano renderizzati e poi il fallback veniva committato.


In React 19, quando un componente sospende, il fallback viene committato e poi i sibling sospesi vengono renderizzati.
Questa modifica significa che i fallback Suspense vengono visualizzati più velocemente, continuando a pre-riscaldare le richieste lazy nell’albero sospeso.
Rimosse build UMD
UMD era ampiamente usato in passato come modo conveniente per caricare React senza un passo di build. Ora esistono alternative moderne per caricare moduli come script nei documenti HTML. A partire da React 19, React non produrrà più build UMD per ridurre la complessità del processo di test e rilascio.
Per caricare React 19 con un tag script, consigliamo di usare una CDN basata su ESM come esm.sh.
<script type="module">
import React from "https://esm.sh/react@19/?dev"
import ReactDOMClient from "https://esm.sh/react-dom@19/client?dev"
...
</script>Le librerie che dipendono dagli internals di React possono bloccare gli upgrade
Questo rilascio include modifiche agli internals di React che possono impattare librerie che ignorano le nostre richieste di non usare internals come SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED. Queste modifiche sono necessarie per far atterrare i miglioramenti in React 19 e non romperanno librerie che seguono le nostre linee guida.
In base alla nostra Versioning Policy, questi aggiornamenti non sono elencati come breaking change e non includiamo documentazione su come aggiornarli. La raccomandazione è rimuovere qualsiasi codice che dipende dagli internals.
Per riflettere l’impatto dell’uso degli internals, abbiamo rinominato il suffisso SECRET_INTERNALS in:
_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE
In futuro bloccheremo più aggressivamente l’accesso agli internals da React per scoraggiarne l’uso e assicurare che gli utenti non siano bloccati dall’upgrade.
Modifiche TypeScript
Rimossi tipi TypeScript deprecati
Abbiamo ripulito i tipi TypeScript in base alle API rimosse in React 19. Alcuni tipi rimossi sono stati spostati in pacchetti più pertinenti e altri non servono più per descrivere il comportamento di React.
Consulta types-react-codemod per l’elenco delle sostituzioni supportate. Se ritieni che manchi un codemod, può essere tracciato nell’elenco dei codemod React 19 mancanti.
Richieste ref cleanup
Questa modifica è inclusa nel preset codemod react-19 come no-implicit-ref-callback-return.
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:
- <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 no.
useRef richiede un argomento
Questa modifica è inclusa nel preset codemod react-19 come refobject-defaults.
Una lamentela di lunga data su come TypeScript e React lavorano insieme riguardava useRef. Abbiamo cambiato i tipi così che useRef ora richiede un argomento. Questo semplifica significativamente la sua firma di tipo. Ora si comporterà più come createContext.
// @ts-expect-error: Expected 1 argument but saw none
useRef();
// Passes
useRef(undefined);
// @ts-expect-error: Expected 1 argument but saw none
createContext();
// Passes
createContext(undefined);Questo significa anche che tutte le ref sono mutabili. Non avrai più il problema per cui non puoi mutare una ref perché l’hai inizializzata con null:
const ref = useRef<number>(null);
// Cannot assign to 'current' because it is a read-only property
ref.current = 1;MutableRef è ora deprecato in favore di un unico tipo RefObject che useRef restituirà sempre:
interface RefObject<T> {
current: T
}
declare function useRef<T>: RefObject<T>useRef ha ancora un overload di convenienza per useRef<T>(null) che restituisce automaticamente RefObject<T | null>. Per facilitare la migrazione dovuta all’argomento obbligatorio per useRef, è stato aggiunto un overload di convenienza per useRef(undefined) che restituisce automaticamente RefObject<T | undefined>.
Consulta [RFC] Make all refs mutable per discussioni precedenti su questa modifica.
Modifiche al tipo TypeScript ReactElement
Questa modifica è inclusa nel codemod react-element-default-any-props.
Le props degli elementi React ora hanno default unknown invece di any se l’elemento è tipizzato come ReactElement. Questo non ti riguarda se passi un type argument a ReactElement:
type Example2 = ReactElement<{ id: string }>["props"];
// ^? { id: string }Ma se ti basavi sul default, ora devi gestire unknown:
type Example = ReactElement["props"];
// ^? Before, was 'any', now 'unknown'Dovresti averne bisogno solo se hai molto codice legacy che si basa su accessi unsound alle props degli elementi. L’introspezione degli elementi esiste solo come escape hatch, e dovresti rendere esplicito che l’accesso alle props è unsound tramite un any esplicito.
Il namespace JSX in TypeScript
Questa modifica è inclusa nel preset codemod react-19 come scoped-jsx
Una richiesta di lunga data è rimuovere il namespace globale JSX dai nostri tipi in favore di React.JSX. Questo aiuta a prevenire l’inquinamento dei tipi globali che previene conflitti tra diverse librerie UI che usano JSX.
Ora dovrai avvolgere l’augmentation del modulo del namespace JSX in `declare module ”…”:
// global.d.ts
+ declare module "react" {
namespace JSX {
interface IntrinsicElements {
"my-element": {
myElementProps: string;
};
}
}
+ }Lo specifier esatto del modulo dipende dalla JSX runtime specificata nelle compilerOptions del tuo tsconfig.json:
- Per
"jsx": "react-jsx"sarebbereact/jsx-runtime. - Per
"jsx": "react-jsxdev"sarebbereact/jsx-dev-runtime. - Per
"jsx": "react"e"jsx": "preserve"sarebbereact.
Tipizzazioni migliori per useReducer
useReducer ora ha un type inference migliorato grazie a @mfp22.
Tuttavia, questo ha richiesto una breaking change in cui useReducer non accetta il tipo reducer completo come type parameter ma invece non ne richiede nessuno (e si affida al contextual typing) o richiede sia lo state che il tipo action.
La nuova best practice è non passare type arguments a useReducer.
- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer(reducer)Questo potrebbe non funzionare in edge case dove puoi tipizzare esplicitamente lo state e l’action, passando l’Action in una tupla:
- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer<State, [Action]>(reducer)Se definisci il reducer inline, incoraggiamo ad annotare i parametri della funzione invece:
- useReducer<React.Reducer<State, Action>>((state, action) => state)
+ useReducer((state: State, action: Action) => state)Questo è anche ciò che dovresti fare se sposti il reducer fuori dalla chiamata useReducer:
const reducer = (state: State, action: Action) => state;Changelog
Altre breaking change
- react-dom: Errore per URL javascript in
srcehref#26507 - react-dom: Rimozione di
errorInfo.digestdaonRecoverableError#28222 - react-dom: Rimozione di
unstable_flushControlled#26397 - react-dom: Rimozione di
unstable_createEventHandle#28271 - react-dom: Rimozione di
unstable_renderSubtreeIntoContainer#28271 - react-dom: Rimozione di
unstable_runWithPriority#28271 - react-is: Rimozione di metodi deprecati da
react-is28224
Altre modifiche rilevanti
- react: Raggruppamento di sync, default e continuous lanes #25700
- react: Nessun prerender dei sibling di un componente sospeso #26380
- react: Rilevamento di loop di aggiornamento infiniti causati da aggiornamenti in fase di render #26625
- react-dom: Le Transitions in popstate sono ora sincrone #26025
- react-dom: Rimozione del warning sugli Effect di layout durante SSR #26395
- react-dom: Warning e nessuna impostazione di stringa vuota per src/href (eccetto tag anchor) #28124
Per l’elenco completo delle modifiche, consulta il Changelog.
Grazie a Andrew Clark, Eli White, Jack Pope, Jan Kassens, Josh Story, Matt Carroll, Noah Lemen, Sophie Alpert e Sebastian Silbermann per la revisione e l’editing di questo post.