React v16.9.0 et feuille de route révisée

2019 M08 8 par Dan Abramov et Brian Vaughn

React 16.9 sort aujourd’hui. Cette version contient quelques nouvelles fonctionnalités, des correctifs et de nouveaux avertissements de dépréciation pour vous aider à préparer votre passage à la prochaine version majeure.

Nouvelles dépréciations

Renommage des méthodes de cycle de vie dangereuses

Il y a plus d’un an, nous annonçions le renommage des méthodes de cycle de vie dangereuses :

  • componentWillMountUNSAFE_componentWillMount
  • componentWillReceivePropsUNSAFE_componentWillReceiveProps
  • componentWillUpdateUNSAFE_componentWillUpdate

React 16.9 ne rompt pas la compatibilité ascendante, et les anciens noms continuent à fonctionner dans cette version. Mais vous verrez désormais un avertissement si vous les utilisez encore :

Attention : componentWillMount a été renommée, et ne devrait plus être utilisée. (Warning: componentWillMount has been renamed, and is not recommended for use.)

Comme le suggère l’avertissement, vous devriez opter pour de meilleures approches pour chacune de ces méthodes dangereuses. Ceci étant dit, vous n’avez peut-être pas le temps de migrer ou de tester ces composants. Dans ce cas, nous vous conseillons d’exécuter un script de « codemod » pour procéder automatiquement à ces renommages :

cd your_project
npx react-codemod rename-unsafe-lifecycles

(Remarquez que la commande est ici npx, pas npm. npx est un utilitaire fourni par défaut avec Node 6+.)

Ce codemod remplacera les anciens noms tels que componentWillMount par leurs équivalents ajustés comme UNSAFE_componentWillMount :

Le codemod en action

Les nouveaux noms tels que UNSAFE_componentWillMount continueront à fonctionner à la fois dans React 16.9 et React 17.x. En revanche, le nouveau préfixe UNSAFE_ fera mieux ressortir les composants qui recourent à des approches problématiques lors des revues de code et sessions de débogage. (Si vous le souhaitez, vous pouvez en décourager l’utilisation au sein de votre appli en activant le mode strict.)

Remarque

Vous pouvez en apprendre davantage sur notre politique de gestion des versions et notre engagement en termes de stabilité.

Dépréciation des URL javascript:

Les URL commençant par javascript: constituent une surface d’attaque dangereuse car il est facile de les inclure verbatim par inadvertance dans une balise de type <a href>, créant ainsi une faille de sécurité :

const userProfile = {
  website: "javascript: alert('je suis un gros pirate')",
};
// Ça déclenchera désormais un avertissement :
<a href={userProfile.website}>Profil</a>

Dans React 16.9, cette approche continuera à fonctionner, mais loguera un avertissement. Si vous utilisez des URL javascript: pour votre logique applicative, essayez plutôt d’utiliser les gestionnaires d’événements de React. (En dernier recours, vous pouvez contourner la protection avec dangerouslySetInnerHTML, mais nous le déconseillons fortement dans la mesure où ça conduit souvent à des failles de sécurité.)

Dans une future version majeure, React lèvera une erreur s’il rencontre une URL javascript:.

Dépréciation des composants « Fabrique »

Avant que la compilation de classes JavaScript grâce à Babel ne devienne monnaie courante, React prenait en charge les composants « fabriques », qui renvoyaient un objet doté d’une méthode render :

function FactoryComponent() {
  return { render() { return <div />; } }
}

Cette approche est déroutante car elle ressemble trop à une fonction composant—alors que ce n’en est pas une. (Dans cet exemple, une fonction composant se contenterait de renvoyer le <div />.)

On ne trouve cette approche presque nulle part en production, et sa prise en charge rend React un brin plus gros et plus lent que nécessaire. C’est pourquoi nous la déprécions avec la 16.9, et loguons un avertissement si nous la rencontrons. Si vous vous en servez, une solution de contournement possible consiste à rajouter FactoryComponent.prototype = React.Component.prototype. Vous pouvez aussi choisir de convertir un tel composant soit sous forme de classe, soit en tant que fonction composant.

Nous estimons que cette dépréciation aura un impact très limité.

Nouvelles fonctionnalités

act() asynchrone pour vos tests

React 16.8 a introduit une nouvelle fonction utilitaire pour les tests appelée act(), qui vise à rapprocher le comportement de vos tests de celui au sein d’un navigateur. Par exemple, de multiples mises à jour de l’état local au sein d’un même act() sont traitées comme un lot. Ça correspond au comportement existant de React lorsqu’il gère de véritables événements navigateur, et vous aide à préparer vos composants pour un avenir dans lequel React regroupera davantage les mises à jour sous forme de lots.

Cependant, dans la 16.8 act() n’autorisait que les fonctions synchrones. Vous pouviez parfois rencontrer un avertissement comme ce qui suit lors d’un test, mais ce n’était pas facile à corriger :

An update to SomeComponent inside a test was not wrapped in act(...).

(Une mise à jour de SomeComponent dans un test n’a pas été enrobée par act(…), NdT)

Avec React 16.9, act() autorise aussi les fonctions asynchrones, et vous pouvez faire un await sur son appel :

await act(async () => {
  // ...
});

Voilà qui règle les cas où vous ne pouviez pas utiliser act() jusque-là, par exemple une mise à jour de l’état local au sein d’une fonction asynchrone. Résultat, vous devriez enfin pouvoir éliminer les avertissements restants sur act() au sein de vos tests.

Vous êtes également nombreux à nous avoir signalé qu’on ne trouvait pas assez d’informations sur la façon d’écrire des tests avec act(). Le nouveau guide Recettes de test décrit les scénarios courants, et explique en quoi act() peut vous aider à écrire de bons tests. Les exemples utilisent les API DOM natives, mais vous pouvez utiliser React Testing Library pour réduire le volume de code générique. Nombre de ses méthodes utilisent déjà act() en interne.

Si vous rencontrez d’autres scénarios dans lesquels act() ne fonctionne pas bien pour vous, n’hésitez pas à ouvrir un ticket et nous tenterons de vous aider.

Mesurer les performances avec <React.Profiler>

Dans React 16.5 nous avions introduit un nouveau profileur React pour les DevTools, qui aidait à repérer les goulots d’étranglement dans les performances de votre application. Avec React 16.9, nous ajoutons une manière programmatique de collecter ces mesures, appelée <React.Profiler>. Nous estimons que la plupart des petites applis ne s’en serviront pas, mais que ça peut s’avérer utile pour pister les régressions de performance au fil du temps dans les applis de plus grande taille.

Le <Profiler> mesure la fréquence des rendus d’une appli React et le « coût » de ces rendus. L’objectif est de vous aider à identifier les parties d’une application qui sont lentes et pourraient bénéficier d’optimisations telles que la mémoïsation.

Vous pouvez ajouter un <Profiler> dans une arborescence React pour mesurer le coût de rendu de cette partie de l’arbre de composants. Le composant requiert deux props : un id (chaîne de caractères) et une fonction de rappel onRender, que React appelle chaque fois qu’un composant au sein de l’arbre « finalise » (commits, NdT) une mise à jour.

render(
  <Profiler id="application" onRender={onRenderCallback}>
    <App>
      <Navigation {...props} />
      <Main {...props} />
    </App>
  </Profiler>
);

Pour en apprendre davantage sur le Profiler et sur les paramètres passés à la fonction de rappel onRender, jetez un coup d’œil aux docs de Profiler.

Remarque

Le profilage pénalise légèrement les performances effectives, il est donc désactivé dans le build de production.

Pour activer le profilage en production, React fournit un build de production spécifique avec le profilage activé. Vous pouvez apprendre comment l’utiliser sur fb.me/react-profiling.

Correctifs importants

Cette version comprend quelques autres améliorations notables :

  • Nous avons corrigé un crash qui survenait lorsqu’on appelait findDOMNode() dans une arborescence <Suspense>.
  • Nous avons corrigé une fuite de mémoire due à des sous-arbres qui restaient référencés après leur retrait.
  • En cas de boucle infinie due à un setState au sein d’un useEffect, nous loguons désormais une erreur. (Notez la cohérence avec l’erreur que l’on constate lorsqu’on appelle setState dans le componentDidUpdate d’une classe.)

Nous aimerions en profiter pour remercier tous les contributeurs qui nous ont aidé à faire émerger puis corriger ces bugs et d’autres. Vous pouvez retrouver la liste complète plus bas.

Une feuille de route révisée

En novembre 2018, nous avions publié la feuille de route suivante pour les versions 16.x :

  • Une version 16.x mineure avec les Hooks (initialement prévue au Q1 2019)
  • Une version 16.x mineure avec le mode concurrent (initialement prévue au Q2 2019)
  • Une version 16.x mineure avec Suspense pour la récupération de données (initialement prévue à la mi-2019)

Ces prévisions étaient trop optimistes, et nous avons dû les ajuster.

TL;PL : nous avons sorti les Hooks dans les temps, mais nous regroupons le mode concurrent et Suspense pour la récupération de données dans une unique version que nous avons l’intention de sortir d’ici la fin de l’année.

En février, nous avons sorti la version stable 16.8 avec les Hooks React, suivie un mois plus tard par leur prise en charge dans React Native. Néanmoins, nous avons sous-estimé le travail restant pour cette version, y compris les règles d’analyse statique (linting, NdT), les outils de développement, les exemples et les compléments de documentation. Tout ça a décalé la chronologie de quelques mois.

À présent que les Hooks sont sortis, le travail sur le mode concurrent et Suspense pour la récupération de données est en plein essor. Le nouveau site Facebook, actuellement en développement, est construit à l’aide de ces fonctionnalités. Les tester sur du véritable code de production nous a permis de découvrir et corriger de nombreux problèmes avant qu’ils n’affectent nos utilisateurs open source. Certains de ces correctifs ont nécessité des changements de conception pour ces fonctionnalités, ce qui a contribué à décaler la chronologie de sortie.

Forts de cette expérience, voici comment nous avons l’intention de procéder.

Une version au lieu de deux

Le mode concurrent et Suspense sont au cœur du nouveau site Facebook actuellement en plein développement, c’est pourquoi nous avons de bonnes raisons de penser qu’elles sont près de se stabiliser techniquement. Nous avons par ailleurs une meilleure compréhension désormais des étapes concrètes à suivre pour leur adoption dans l’open source.

À l’origine nous pensions pouvoir découper le mode concurrent et Suspense pour la récupération de données à travers deux versions. Nous avons constaté que ce séquençage allait être plus déroutant qu’autre chose car ces deux fonctionnalités sont plus étroitement liées que nous ne le pensions au départ. C’est pourquoi nous comptons plutôt sortir une prise en charge à la fois du mode concurrent et de Suspense pour la récupération de données dans une unique version combinée.

Nous ne voulons pas à nouveau trop nous avancer sur la date de sortie. Dans la mesure où nous nous reposons sur ces deux fonctionnalités pour du code de production, nous estimons que nous pourrons sortir cette année une version 16.x dans laquelle vous pourrez choisir de les utiliser.

Des nouvelles de la récupération de données

React n’est pas prescriptif sur la façon de récupérer les données, alors la première version de Suspense pour la récupération de données se concentrera probablement sur l’intégration avec des bibliothèques de récupération de données prescriptives. Par exemple, chez Facebook nous utilisons une API expérimentale de Relay qui s’intègre avec Suspense. Nous ferons en sorte que les autres bibliothèques de ce genre aient la documentation nécessaire pour mettre en place des intégrations similaires.

Pour la première version, nous ne souhaitons pas nous concentrer sur la solution ad hoc au besoin « lancer une requête HTTP » utilisée dans nos démos antérieures (également connue sous le terme « React Cache »). Ceci dit, il est probable que notre équipe et la communauté React exploreront ce type de sujet dans les mois qui suivront la sortie initiale.

Des nouvelles du rendu côté serveur

Nous avons démarré le travail sur le nouveau moteur de rendu côté serveur compatible avec Suspense, mais il ne sera probablement pas prêt pour la version initiale du mode concurrent. Cette version fournira, ceci dit, une solution temporaire qui permettra au moteur de rendu côté serveur existant de produire immédiatement du HTML avec des appels de repli Suspense, pour ensuite produire le véritable contenu côté client. Nous utilisons actuellement nous-mêmes cette solution chez Facebook en attendant que le moteur de rendu en streaming soit prêt.

Pourquoi est-ce si long ?

Nous avons publié les éléments préalables au mode concurrent à chaque fois qu’ils atteignaient la stabilité, y compris la nouvelle API de contexte, le chargement différé avec Suspense, et les Hooks. Nous avons hâte de sortir les parties manquantes, mais auparavant les essayer à grande échelle est une partie importante de notre processus. Pour être honnêtes, ça représentait juste davantage de travail que ce qu’on estimait initialement. Comme toujours, nous serons ravis de lire vos questions et retours sur Twitter et dans notre gestion de tickets.

Installation

React

React v16.9.0 est disponible sur le référentiel npm.

Pour installer React 16 avec Yarn, exécutez :

yarn add react@^16.9.0 react-dom@^16.9.0

Pour installer React 16 avec npm, exécutez :

npm install --save react@^16.9.0 react-dom@^16.9.0

Nous fournissons aussi des builds de React sur un CDN :

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

Consultez la documentation pour des instructions d’installation détaillées.

Changelog

React

  • Ajout de l’API <React.Profiler> pour collecter programmatiquement des mesures de performances. (@bvaughn dans #15172)
  • Retrait de unstable_ConcurrentMode en faveur de unstable_createRoot. (@acdlite dans #15532)

React DOM

  • Dépréciation des anciens noms pour les méthodes de cycle de vie UNSAFE_*. (@bvaughn dans #15186 et @threepointone dans #16103)
  • Dépréciation des URL javascript: en tant que surfaces d’attaque courantes. (@sebmarkbage dans #15047)
  • Dépréciation des composants rarement utilisés “module pattern” (fabrique). (@sebmarkbage dans #15145)
  • Prise en charge de l’attribut disablePictureInPicture sur <video>. (@eek dans #15334)
  • Prise en charge de l’événement onLoad sur <embed>. (@cherniavskii dans #15614)
  • Capacité à modifier l’état de useState dans les DevTools. (@bvaughn dans #14906)
  • Capacité à (dés)activer Suspense dans les DevTools. (@gaearon dans #15232)
  • Avertir de l’utilisation de setState au sein d’un useEffect, qui crée une boucle. (@gaearon dans #15180)
  • Correction d’une fuite de mémoire. (@paulshen dans #16115)
  • Correction d’un crash dans findDOMNode pour les composants enrobés par <Suspense>. (@acdlite dans #15312)
  • Correction du traitement trop tardif d’effets en attente. (@acdlite dans #15650)
  • Correction d’un ordre incorrect d’arguments dans un message d’avertissement. (@brickspert dans #15345)
  • Correction du masquage des nœuds de secours de Suspense en présence d’un style !important. (@acdlite dans #15861 and #15882)
  • Légère amélioration des performances de l’hydratation. (@bmeurer dans #15998)

React DOM Server

  • Corrections d’une sortie incorrecte pour les noms de propriétés CSS personnalisées en casse camel. (@bedakb dans #16167)

Utilitaires de test React et moteur de rendu de test