React 19 RC : guide de migration
Le 25 avril 2024 par Ricky Hanlon
Les améliorations apportées par React 19 RC nécessitent quelques ruptures de compatibilité, mais nous avons travaillé dur pour faciliter la mise à jour le plus possible, et nous ne nous attendons pas à ce que ces changements impactent la majorité des applications.
Dans cet article, nous vous guidons à travers les étapes nécessaires à une migration vers React 19 :
- Installation
- Codemods
- Ruptures de compatibilité ascendante
- Nouvelles dépréciations
- Changements notables
- Changements liés à TypeScript
- Changelog
Si vous aimeriez nous aider à tester React 19, suivez les étapes de ce guide de migration et signalez-nous tout problème que vous rencontreriez. Pour une liste des nouveautés de React 19, consultez l’annonce de sortie de React 19.
Installation
Pour installer la dernière version de React et React DOM :
npm install --save-exact react@rc react-dom@rc
Ou si vous utilisez Yarn :
yarn add --exact react@rc react-dom@rc
Si vous utilisez TypeScript, vous aurez aussi besoin de mettre à jour les types. Une fois que React 19 sortira en version stable, vous pourrez installer les types au travers des paquets habituels @types/react
et @types/react-dom
. D’ici là, ces types sont mis à disposition par des paquets distincts que vous devrez forcer dans votre package.json
:
{
"dependencies": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc"
},
"overrides": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc"
}
}
Nous fournissons par ailleurs un codemod pour les remplacements les plus courants. Consultez par exemple la section Changements liés à TypeScript plus loin.
Codemods
Pour vous aider à migrer, nous avons travaillé avec l’équipe de codemod.com pour publier des codemods qui vont automatiquement mettre à jour votre code vers la plupart des nouvelles API et approches à jour de React 19.
Tous ces codemods sont disponibles au travers du dépôt react-codemod
et l’équipe de Codemod nous aide à les maintenir. Pour les exécuter, nous vous conseillons la commande codemod
plutôt que react-codemod
parce qu’elle est plus rapide, permet des migrations plus complexes, et fournit une meilleure gestion de TypeScript.
Dans le reste de cet article, les changements proposant un codemod indiquent la commande à employer.
Pour une liste complète des codemods disponibles, consultez le dépôt react-codemod
.
Ruptures de compatibilité ascendante
Les erreurs lors du rendu ne sont pas propagées
Dans les versions précédentes de React, les erreurs survenant lors du rendu étaient interceptées puis relancées. En mode développement, nous les affichions également avec console.error
, ce qui pouvait entraîner des doublons dans les journaux d’erreurs.
Avec React 19, nous avons amélioré la gestion des erreurs pour réduire cette duplication en évitant de propager ces erreurs :
- Erreurs non interceptées : les erreurs non interceptées par un Périmètre d’Erreurs sont signalées par
window.reportError
. - Erreurs interceptées : les erreurs interceptées par un Périmètre d’Erreurs sont signalées par
console.error
.
Ce changement ne devrait pas impacter la majorité des applis, mais si votre signalement d’erreur en production dépend de la propagation des erreurs, vous aurez peut-être besoin de le mettre à jour. Pour permettre ça, nous avons ajouté des méthodes à createRoot
et hydrateRoot
qui permettent de personnaliser la gestion des erreurs :
const root = createRoot(container, {
onUncaughtError: (error, errorInfo) => {
// ... faire un rapport d’erreur
},
onCaughtError: (error, errorInfo) => {
// ... faire un rapport d’erreur
}
});
Pour en savoir plus, consultez les documentations de createRoot
et hydrateRoot
.
Retrait d’API React dépréciées
Retirés : propTypes
et defaultProps
sur les fonctions
Les PropTypes
étaient dépréciées depuis avril 2017 (v15.5.0).
Avec React 19, nous retirons la vérification de propTypes
du paquet React, et son utilisation sera silencieusement ignorée. Si vous utilisez encore propTypes
, nous vous conseillons de passer à TypeScript ou une autre solution de vérification de types.
Nous retirons également la gestion de defaultProps
pour les fonctions composants, au profit des valeurs par défaut de paramètres fournies par ES6. Les composants à base de classes continuent à prendre en charge defaultProps
, puisqu’il n’y a pas de syntaxe ES6 équivalente.
// Avant
import PropTypes from 'prop-types';
function Heading({text}) {
return <h1>{text}</h1>;
}
Heading.propTypes = {
text: PropTypes.string,
};
Heading.defaultProps = {
text: 'Salut tout le monde !',
};
// Après
interface Props {
text?: string;
}
function Heading({text = 'Salut tout le monde !'}: Props) {
return <h1>{text}</h1>;
}
Retirés : les contextes historiques basés sur contextTypes
et getChildContext
La gestion historique des contextes était dépréciée depuis octobre 2018 (v16.6.0).
La gestion historique des contextes n’était disponible que pour les composants à base de classes au travers des API contextTypes
et getChildContext
, et a été remplacée par contextType
en raison de bugs subtils difficiles à repérer. Avec React 19, nous retirons la gestion historique des contextes pour rendre React un peu plus léger et rapide.
Si vous utilisiez encore les contextes historiques dans des composants à base de classes, vous devrez migrer vers l’API contextType
qui les remplace :
// Avant
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>;
}
}
// Après
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>;
}
}
Retirées: les refs textuelles
Les refs textuelles (à base de string
) étaient dépréciées depuis mars 2018 (v16.3.0).
Les composants à base de classes permettaient des refs textuelles avant que celles-ci soient dépréciées au profit de refs par fonction de rappel, en raison de leurs nombreux inconvénients. Avec React 19, nous retirons les refs textuelles pour rendre React plus simple et plus facile à comprendre.
Si vous utilisez encore des refs textuelles dans les composants à base de classes, vous devrez migrer vers des refs par fonction de rappel :
// Avant
class MyComponent extends React.Component {
componentDidMount() {
this.refs.input.focus();
}
render() {
return <input ref='input' />;
}
}
// Après
class MyComponent extends React.Component {
componentDidMount() {
this.input.focus();
}
render() {
return <input ref={input => this.input = input} />;
}
}
Retirées: les composants « Fabrique »
Les composants « Fabrique » (Module pattern factories — NdT) étaient dépréciées depuis août 2019 (v16.9.0).
Cette approche était rarement utilisée, et sa prise en charge alourdissait inutilement React. Avec React 19, nous retirons la prise en charge des composants « Fabrique », qu’il vous faudrait migrer vers des fonctions classiques :
// Acant
function FactoryComponent() {
return { render() { return <div />; } }
}
// Après
function FactoryComponent() {
return <div />;
}
Retirée : React.createFactory
createFactory
était dépréciée depuis février 2020 (v16.13.0).
Il était courant de recourir à createFactory
avant que JSX devienne suffisamment répandu, mais elle est très rarement utilisée de nos jours, et peut être remplacée par JSX. Avec React 19, nous retirons createFactory
que vous devriez migrer vers JSX :
// Avant
import { createFactory } from 'react';
const button = createFactory('button');
// Après
const button = <button />;
Retiré : react-test-renderer/shallow
Dans React 18, nous avions mis à jour react-test-renderer/shallow
pour re-exporter react-shallow-renderer. Avec React 19, nous retirons react-test-render/shallow
au profit d’une installation directe du bon paquet :
npm install react-shallow-renderer --save-dev
- import ShallowRenderer from 'react-test-renderer/shallow';
+ import ShallowRenderer from 'react-shallow-renderer';
Retrait d’API React DOM dépréciées
Retiré : react-dom/test-utils
Nous avons déplacé act
de react-dom/test-utils
vers le paquet 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.(« ReactDOMTestUtils.act
est dépréciée en faveur de React.act
. Importez act
depuis react
plutôt que react-dom/test-utils
. Consultez https://fr.react.dev/warnings/react-dom-test-utils pour davantage d’informations. » — NdT)
Pour corriger cet avertissement, importez act
depuis react
:
- import {act} from 'react-dom/test-utils'
+ import {act} from 'react';
Toutes les autres fonctions de test-utils
ont été retirées. Ces utilitaires étaient rarement employés, et encourageaient à tort une dépendance à des détails d’implémentation de bas niveau de vos composants et de React. Avec React 19, ces fonctions lèveront une erreur lors de l’appel, et leurs exports seront retirés lors d’une future version.
Consultez la page d’avertissement pour les alternatives possibles.
Retirée : ReactDOM.render
ReactDOM.render
était dépréciée depuis mars 2022 (v18.0.0). Avec React 19, nous retirons ReactDOM.render
, qu’il vous faudrait migrer vers ReactDOM.createRoot
:
// Avant
import {render} from 'react-dom';
render(<App />, document.getElementById('root'));
// Après
import {createRoot} from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
Retirée : ReactDOM.hydrate
ReactDOM.hydrate
était dépréciée depuis mars 2022 (v18.0.0). Avec React 19, nous retirons ReactDOM.hydrate
qu’il vous faudrait migrer vers ReactDOM.hydrateRoot
:
// Avcant
import {hydrate} from 'react-dom';
hydrate(<App />, document.getElementById('root'));
// Après
import {hydrateRoot} from 'react-dom/client';
hydrateRoot(document.getElementById('root'), <App />);
Retirée : unmountComponentAtNode
ReactDOM.unmountComponentAtNode
était dépréciée depuis mars 2022 (v18.0.0). Avec React 19, vous devrez utiliser plutôt root.unmount()
.
// Avant
unmountComponentAtNode(document.getElementById('root'));
// Après
root.unmount();
Pour en apprendre davantage, allez voir les sections sur root.unmount()
dans les documentations de createRoot
et hydrateRoot
.
Retirée : ReactDOM.findDOMNode
ReactDOM.findDOMNode
était dépréciéee depuis octobre 2018 (v16.6.0).
Nous retirons findDOMNode
parce qu’il s’agit d’un échappatoire historique particulièrement lent à exécuter, fragile à refactorer, ne renvoyant que le premier enfant, et qui mélangeait les niveaux d’abstraction (apprenez-en davantage ici). Vous pouvez remplacer ReactDOM.findDOMNode
par des refs DOM :
// Avant
import {findDOMNode} from 'react-dom';
function AutoselectingInput() {
useEffect(() => {
const input = findDOMNode(this);
input.select()
}, []);
return <input defaultValue="Salut" />;
}
// Après
function AutoselectingInput() {
const ref = useRef(null);
useEffect(() => {
ref.current.select();
}, []);
return <input ref={ref} defaultValue="Salut" />
}
Nouvelles dépréciations
Déprécié : element.ref
React 19 considère ref
comme une prop, de sorte que nous déprécions element.ref
au profit de element.props.ref
.
Si vous accédez à element.ref
, vous obtiendrez un avertissement :
(« L’accès à element.ref n’est plus pris en charge. Les refs sont désormais des props classiques. La ref sera retirée du type élément JSX dans une prochaine version. » — NdT)
Déprécié : react-test-renderer
Nous déprécions react-test-renderer
parce qu’il implémente son propre environnement de rendu, qui ne correspond pas aux environnements des utilisateurs, encourage la dépendance à des détails d’implémentation, et s’appuie sur l’introspection de structures internes à React.
Ce moteur de rendu de test a été créé avant que des stratégies de test viables soient disponibles, telles que React Testing Library, et nous conseillons désormais d’utiliser plutôt une bibliothèque de test moderne.
Avec React 19, react-test-renderer
affiche un avertissement de dépréciation, et recourt désormais à du rendu concurrent. Nous vous conseillons de migrer vos tests vers @testing-library/react ou @testing-library/react-native pour une expérience de test plus moderne et mieux maintenue.
Changements notables
Modifications du mode strict
React 19 apporte plusieurs correctifs et améliorations au mode strict.
Lors du double rendu du mode strict en développement, useMemo
et useCallback
réutiliseront le résultat mémoïsé du premier rendu lors du second rendu. Les composants qui étaient déjà compatibles avec le mode strict ne devraient constater aucun changement de comportement.
Comme pour tous les comportements du mode strict, il s’agit de faire proactivement émerger des bugs dans vos composants lors du développement, de façon à ce que vous puissiez les corriger avant qu’ils n’atteignent la production. En développement, le mode strict fait par exemple deux appels aux fonctions de rappel des refs lors du montage initial, pour simuler ce qui se passe lorsqu’un composant monté est remplacé par un affichage Suspense de secours.
Améliorations de Suspense
Avec React 19, lorsqu’un composant suspend, React committera immédiatement le rendu de secours du périmètre Suspense le plus proche, sans attendre que l’arbre de composants concerné fasse un rendu intégral. Une fois le commit du rendu de secours terminé, React planifie un nouveau rendu des composants suspendus pour « préchauffer » les requêtes paresseuses du reste de l’arbre :
Grâce à ce changement, les contenus de secours Suspense sont affichés plus tôt, et les requêtes paresseuses sont préchauffées au sein de l’arbre suspendu.
Builds UMD retirés
UMD était largement utilisé par le passé, en tant que moyen pratique d’utiliser React sans étape de build. Il existe aujourd’hui des façons modernes de charger des modules en tant que scripts dans des documents HTML. À partir de React 19, React ne fournira plus de builds UMD afin de réduire la complexité de ses processus de tests et de livraison.
Pour charger React 19 ay moyen d’une balise script, nous vous conseillons un CDN compatible ESM, tel qu’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>
Les bibliothèques basées sur des détails d’implémentation de React risquent d’être bloquantes
Cette version inclut des changements à la mécanique interne de React qui sont susceptibles d’impacter des bibliothèques qui auraient persisté à ignorer nos demandes implorantes de ne pas en dépendre, des éléments tels que SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
. Ces modifications sont nécessaires pour permettre l’arrivée de certaines améliorations dans React 19, et ne casseront aucune bibliothèque qui suivrait nos recommandations.
Au regard de notre politique de versions, ces mises à jour ne sont pas listées comme des ruptures de compatibilité ascendante, et nous ne fournissons pas de documentation liée à leur migration. Notre recommandation reste de retirer tout code basé sur ces détails internes.
Pour refléter l’impact du recours à ces détails internes, nous avons renommé le suffixe SECRET_INTERNALS
vers _DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE
.
À l’avenir, nous bloquerons de façon plus agressive l’accès aux détails internes de React pour en décourager l’utilisation et nous assurer que les utilisateurs ne seront pas bloqués sur leurs chemins de migration.
Changements liés à TypeScript
Retrait de types TypeScript dépréciés
Nous avons retiré les types TypeScript basés sur des API retirées de React 19. Certains des types retirés ont été déplacés vers des paquets plus appropriés, et d’autres ne sont tout simplement plus nécessaires pour décrire le comportement de React.
Consultez types-react-codemod
pour une liste des remplacements pris en charge. Si vous estimez qu’un codemod est manquant, vous pouvez suivre la liste des codemods React 19 manquants.
Les fonctions de nettoyage de ref
deviennent vérifiées
Cette modification fait partie du codemod react-19
sous le nom no-implicit-ref-callback-return
.
Suite à l’introduction des fonctions de nettoyage de ref, TypeScript refusera désormais que vous renvoyiez quoi que ce soit d’autres depuis une fonction de rappel de ref. Le correctif consiste en général à éviter les renvois implicites :
- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />
Le code original renvoyait l’instance de HTMLDivElement
et TypeScript ne pouvait savoir si vous visiez ou non une fonction de nettoyage.
useRef
nécessite un argument
Cette modification fait partie du codemod react-19
sous le nom refobject-defaults
.
Un des anciens reproches liés à TypeScript et React concernait useRef
. Nous avons ajusté nos types de façon à ce queuseRef
exige désormais un argument. Ça simplifie considérablement sa signature de type. Elle se comporte désormais davantage comme createContext
.
// @ts-expect-error: attendait un argument mais n’en a aucun
useRef();
// Ça type
useRef(undefined);
// @ts-expect-error: attendait un argument mais n’en a aucun
createContext();
// Ça type
createContext(undefined);
Ça signifie aussi que toutes les refs sont désormais mutables. Vous ne rencontrerez plus le problème où vous ne pouviez pas muter une ref parce que vous l’aviez initialisée avec null
:
const ref = useRef<number>(null);
// Impossible d’assigner à `current` parce qu’elle est perçue comme en lecture seule
ref.current = 1;
MutableRef
est désormais déprécié au profit d’un unique type RefObject
que useRef
renverra toujours :
interface RefObject<T> {
current: T
}
declare function useRef<T>: RefObject<T>
useRef
a toujours une surcharge de confort pour useRef<T>(null)
qui renvoie automatiquement RefObject<T | null>
. Pour faciliter la migration liée à l’exigence d’un argument à useRef
, une surcharge de confort pour useRef(undefined)
a été ajoutée qui renvoie automatiquement RefObject<T | undefined>
.
Consultez la [RFC] Rendre toutes les refs mutables (en anglais) pour les discussions qui ont mené à ce changement.
Changements au type TypeScript ReactElement
Cette modification fait partie du codemod react-element-default-any-props
.
Les props
des éléments React ont désormais comme type par défaut unknown
plutôt que any
si l’élément est typé comme ReactElement
. Ça ne vous impacte pas si vous passiez un argument de type à ReactElement
:
type Example2 = ReactElement<{ id: string }>["props"];
// ^? { id: string }
Mais si vous vous basiez sur les types par défaut, il vous faut maintenant gérer unknown
:
type Example = ReactElement["props"];
// ^? Avant, c’était typé 'any', mais maintenant 'unknown'
Vous ne devriez en avoir besoin que si vous avez beaucoup de code historique basé sur un accès fragile aux props de l’élément. L’introspection d’élément n’existe qu’au titre d’échappatoire et vous devriez toujours être explicite sur la fragilité de votre accès aux props en utilisant par exemple un any
explicite.
L’espace de noms JSX en TypeScript
Cette modification fait partie du codemod react-19
sous le nom scoped-jsx
.
On nous demandait de longue date de retirer l’espace de noms global JSX
de nos types, au profit de React.JSX
. L’idée était d’éviter une pollution des types globaux, réduisant par là les conflits entre diverses bibliothèques d’UI utilisant JSX.
Vous aurez désormais besoin d’enrober vos augmentations de modules pour l’espace de noms JSX avec un declare module "..."
:
// global.d.ts
+ declare module "react" {
namespace JSX {
interface IntrinsicElements {
"my-element": {
myElementProps: string;
};
}
}
+ }
La spécification exacte du module dépendra du moteur JSX que vous avez indiqué dans les compilerOptions
de votre tsconfig.json
:
- Pour
"jsx": "react-jsx"
ça serareact/jsx-runtime
. - Pour
"jsx": "react-jsxdev"
ça serareact/jsx-dev-runtime
. - Pour
"jsx": "react"
et"jsx": "preserve"
ça serareact
.
Meilleur typage de useReducer
useReducer
améliore son inférence de type grâce à @mfp22.
Cependant, ça nécessitait une rupture de compatibilité ascendante car useReducer
n’accepte pas le type complet du réducteur comme paramètre de type, mais plutôt soit n’en nécessite aucun (et repose sur l’inférence), soit nécessite les types de l’état et de l’action.
La nouvelle meilleure pratique consiste à ne pas passer de paramètres de type à useReducer
.
- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer(reducer)
Ça pourrait ne pas fonctionner pour des cas à la marge où il vous faudra passer explicitement les types de l’état et de l’action, en passant Action
dans un tuple :
- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer<State, [Action]>(reducer)
Si vous définissez le réducteur à la volée, nous vous conseillons d’annoter plutôt les paramètres de la fonction :
- useReducer<React.Reducer<State, Action>>((state, action) => state)
+ useReducer((state: State, action: Action) => state)
C’est également ce que vous feriez si vous deviez extraire le réducteur de l’appel à useReducer
:
const reducer = (state: State, action: Action) => state;
Changelog
Autres ruptures de compatibilité ascendante
- react-dom: Erreur sur URL JavaScript dans src/href #26507
- react-dom: Retrait de
errorInfo.digest
dansonRecoverableError
#28222 - react-dom: Retrait de
unstable_flushControlled
#26397 - react-dom: Retrait de
unstable_createEventHandle
#28271 - react-dom: Retrait de
unstable_renderSubtreeIntoContainer
#28271 - react-dom: Retrait de
unstable_runWithPriority
#28271 - react-is: Retrait de méthodes dépréciées dans
react-is
28224
Autres changements notables
- react: Traitement par lot des files sync, default et continuous #25700
- react: Pas de prérendu des adelphes d’un composant suspendu #26380
- react: Détecte les boucles infinies dues à des mises à jour en phase de rendu #26625
- react-dom: Les Transitions en popstate sont désormais synchrones #26025
- react-dom: Retire l’avertissement des Effets de layout lors du SSR #26395
- react-dom: Avertit et évite les chaînes vides pour src/href (sauf sur balises d’ancres) #28124
Nous publierons un changelog complet avec la version stable de React 19.
Merci à Andrew Clark, Eli White, Jack Pope, Jan Kassens, Josh Story, Matt Carroll, Noah Lemen, Sophie Alpert et Sebastian Silbermann pour avoir révisé et mis à jour cet article.