Quoi de neuf dans React 18 ? – Informatique CloudSavvy
React 18 sera la prochaine version majeure de la populaire bibliothèque de composants JavaScript. Désormais disponible en tant que release candidate, il introduit plusieurs changements pour améliorer les récupérations de données, les performances et le rendu côté serveur.
Pour tirer parti de toutes les fonctionnalités, vous devrez mettre à niveau votre projet et vous rencontrerez peut-être des modifications avec rupture. Cependant, React 18 est toujours généralement rétrocompatible avec le code plus ancien. Vous devriez être en mesure de modifier la version de publication dans votre package.json
sans faire face à trop de problèmes immédiats.
Sommaire
Rendu simultané
La motivation derrière la plupart des révisions de React 18 concerne ce qu’on appelle le « rendu simultané ». Ce mécanisme donne à React un moyen d’assembler simultanément plusieurs versions de votre arborescence de composants. Bien que les détails de cela ne concernent que les composants internes de la bibliothèque, le résultat est une flexibilité accrue et des performances améliorées pour votre application.
Le rendu simultané rend le processus de rendu interruptible. Alors qu’un rendu dans React 17 doit être terminé une fois qu’il a commencé, React 18 offre un moyen de faire une pause à mi-parcours et de le reprendre plus tard.
Cette capacité signifie que les rendus React sont moins susceptibles d’avoir un impact sur les performances globales du navigateur. Jusqu’à présent, les événements du navigateur tels que les pressions sur les touches et les peintures étaient bloqués pendant qu’un rendu était en cours. Lorsque le rendu simultané est activé, une pression sur une touche interrompra le rendu, permettra au navigateur de gérer le changement, puis reprendra le rendu.
Cela se traduit par une expérience utilisateur plus fluide qui est moins susceptible de bégayer lorsque le rendu coïncide avec d’autres activités. React maintient plusieurs branches de travail; une fois que le rendu d’une branche est terminé, ce qui peut se produire sur plusieurs sessions distinctes, il est accepté dans la branche principale qui produit l’interface utilisateur visible.
Mode simultané vs rendu simultané
Avant que React 18 n’atteigne le statut alpha, cette fonctionnalité était appelée « mode simultané ». Vous pouvez toujours voir ce nom dans des articles et de la documentation plus anciens. Le concept de rendu simultané en tant que mode séparé n’existe plus dans React 18. Cela permet aux applications existantes de passer plus facilement à la nouvelle approche.
Le rendu simultané est fondamentalement différent du système de rendu existant. Il a une toute nouvelle API qui remplace le familier ReactDOM.render()
. À l’époque du mode simultané, la simultanéité était tout ou rien : elle était soit activée pour votre application, avec la perspective de changements majeurs avec rupture, soit complètement hors limites. Maintenant, il est géré plus gracieusement avec React appliquant uniquement le rendu simultané aux mises à jour DOM qui nécessitent réellement une fonctionnalité simultanée.
La nouvelle API racine (activation du mode simultané)
Les applications existantes mises à niveau vers React 18 peuvent continuer à utiliser ReactDOM.render()
dans un avenir prévisible. Cela rendra votre application sans prise en charge de la simultanéité, en utilisant le moteur de rendu familier de la v17. Vous verrez un avertissement de console indiquant que l’API n’est plus prise en charge, mais cela peut être ignoré lors de la mise à niveau.
import App from "./App.js"; import ReactDOM from "react-dom"; // "ReactDOM.render is no longer supported in React 18" ReactDOM.render(<App />, document.getElementById("root"));
Pour supprimer l’avertissement, passez au nouveau createRoot()
API :
import {createRoot} from "react-dom/client"; const root = createRoot(document.getElementById("root")); root.render(<App />);
createRoot()
renvoie un nouvel objet racine qui représente une surface de rendu React. Vous pouvez appeler son render()
méthode pour rendre un composant React à la racine. Le résultat du code ci-dessus est le même que le précédent ReactDOM.render()
Exemple. createRoot()
est une interface plus orientée objet avec une facilité d’utilisation améliorée.
Racines produites par createRoot()
prend en charge le rendu simultané. La mise à niveau vers cette API vous donne un accès opt-in aux nouvelles fonctionnalités de React 18.
le createRoot()
équivalent de ReactDOM.unmountComponentAtNode()
est le nouveau unmount()
méthode exposée sur les objets racine. Vous pouvez l’utiliser pour détacher votre arborescence de composants React et arrêter le rendu de votre application :
import App from "./App.js"; import ReactDOM from "react-dom"; // OLD ReactDOM.unmountComponentAtNode(document.getElementById("root")); // NEW const root = createRoot(document.getElementById("root")); root.unmount();
Fonctionnalités simultanées
Le rendu simultané vous permet d’utiliser des fonctionnalités simultanées pour améliorer les performances de votre application. Voici quelques-unes des principales API disponibles.
Le suspense
le <Suspense>
Le composant existe depuis React 16. Il vous permet d’empêcher le rendu des enfants d’un composant tant qu’une condition n’est pas remplie. Il est couramment utilisé pour les récupérations de données et les importations de modules asynchrones.
const fetchPostHistory = id => fetch(`/users/${id}/posts`); const UserCard = ({Id, Name}) => { const [postHistory] = useState(() => fetchPostHistory(Id)); <div> <h1>{Name}</h1> <React.Suspense fallback="Loading..."> <UserPostHistoryList posts={postHistory} /> <ReportUserLink id={Id} /> </React.Suspense> </div> };
Dans cet exemple, ni le UserPostHistory
ou ReportUserLink
les composants apparaîtront jusqu’à ce que les données de l’historique des publications de l’utilisateur soient extraites du réseau. Cela fonctionne déjà bien dans de nombreuses situations, mais la mise en œuvre de React 17 présente quelques bizarreries.
Si vous enregistriez les effets de chaque composant, vous verriez le ReportUserLink
Le composant a été rendu alors que les messages étaient encore en cours de chargement, même s’il n’est pas visible à ce stade. En utilisant l’explication de la simultanéité plus tôt, il est possible d’expliquer pourquoi : une fois que React a commencé à rendre l’arborescence des composants, il n’avait aucun moyen de s’arrêter, même si un humain peut le repérer. ReportUserLink
est redondant jusqu’à postHistory
est peuplé.
Suspense est plus puissant dans React 18. La nouvelle version s’appelle « Concurrent Suspense » ; l’implémentation précédente est désormais appelée Legacy Suspense. Cela résout le problème de l’exemple ci-dessus : le rendu du même code avec la simultanéité activée empêchera le moteur de rendu d’atteindre <ReportUserLink>
pendant que la récupération des données est en cours.
L’enregistrement des effets de chaque composant montrerait que ReportUserLink
n’est engagé qu’une fois l’historique des publications disponible. React interrompt le rendu lorsqu’il atteint UserPostHistoryList
et doit attendre que les données soient chargées. Une fois l’appel réseau terminé, React reprend le rendu du reste de la sous-arborescence Suspense.
Cette fonctionnalité permet d’éviter un travail inutile dont vos utilisateurs ne bénéficient jamais. Il résout également plusieurs problèmes avec Suspense où les composants peuvent avoir des effets d’exécution plus tôt que prévu. Enfin, cette solution offre une garantie automatique que les données arriveront dans l’ordre où elles ont été demandées. Vous n’avez pas à vous soucier des conditions de concurrence car le rendu est interrompu pendant la récupération des données.
Transitions
Les transitions sont une nouvelle fonctionnalité activée simultanément. Cette API est un moyen de signaler à React les priorités relatives de vos mises à jour d’interface utilisateur. Une « transition » est une mise à jour de priorité relativement faible, telle que le passage d’un écran principal à l’autre. Les mises à jour telles que les nouveaux rendus en réponse à la saisie au clavier et à d’autres interactions de l’utilisateur sont considérées comme plus urgentes.
Marquer une mise à jour comme une transition a quelques effets sur la façon dont React aborde son exécution. React utilisera les capacités de rendu interruptibles de la simultanéité pour suspendre la mise à jour si une mise à jour plus urgente arrive à mi-parcours. Cela aidera à garder votre interface utilisateur réactive aux entrées de l’utilisateur pendant le rendu en cours, réduisant ainsi le bégaiement et le jank.
Les transitions sont utiles dans un large éventail de situations : mettre à jour un volet de notifications dans l’en-tête de votre application, gérer les mises à jour de votre barre latérale et modifier d’autres fonctions auxiliaires de votre interface utilisateur sont tous de bons candidats. Ils fonctionnent également bien pour les actions asynchrones prises en réponse à l’entrée de l’utilisateur, comme le cas classique d’une barre de recherche qui se met à jour au fur et à mesure que l’utilisateur tape.
Cela peut être difficile à obtenir correctement dans React – sans un anti-rebond prudent, il est courant de ressentir un décalage perceptible car les mises à jour causées par la récupération de nouveaux résultats empêchent temporairement le thread principal de gérer les entrées au clavier. Avec React 18, vous pouvez utiliser une transition pour marquer ces mises à jour comme des travaux de faible priorité.
le startTransition()
L’API encapsule les mises à jour d’état sous forme de transitions :
import {startTransition} from "react"; const Component = () => { const [searchQuery, setSearchQuery] = useState(""); const [searchResults, setSearchResults] = useState({}); /** * State updates within the transition function are low-priority */ startTransition(() => { setSearchResults({text: "Search Result 1"}); }); };
Si vous voulez vérifier si une mise à jour est en cours, remplacez plain startTransition()
avec le useTransition()
accrocher. Cela vous donne un booléen indiquant si une transition a du travail en attente.
import {useTransition} from "react"; const Component = () => { const [searchQuery, setSearchQuery] = useState(""); const [searchResults, setSearchResults] = useState({}); const [isSearching, startSearchResultsTransition] = useTransition(); startSearchResultsTransition(() => { setSearchResults({text: "Search Result 1"}); }); return ( <div> <input onChange={setSearchQuery} value={searchQuery} /> <SearchResults results={searchResults} /> {(isSearching && "(Searching...)")} </div> ); };
Toutes les mises à jour d’état existantes sont traitées comme des mises à jour urgentes régulières pour maintenir la compatibilité descendante avec l’ancien code.
Valeurs différées
Les valeurs différées sont un autre moyen de maintenir la réactivité lors des mises à jour de longue durée. Lorsqu’une valeur est différée par le useDeferredValue()
crochet, React continuera à montrer son vieux valeur pendant une période déterminée.
const Component = () => { const [results, setResults] = useState([]); const deferredResults = useDeferredResults(results, {timeoutMs: 5000}); return <ResultsGrid results={deferredResults} />; };
Permettre à React de continuer à afficher les anciens résultats pendant cinq secondes évite les ralentissements en supprimant la nécessité de restituer immédiatement les données récupérées dès leur arrivée. C’est une forme d’anti-rebond intégrée à la gestion d’état de React. Les données peuvent être en retard de quelques secondes sur l’état réel afin de réduire la quantité globale de travail effectuée.
Meilleur dosage
Un dernier changement axé sur les performances dans React 18 consiste en un ensemble d’améliorations du traitement par lot des mises à jour d’état. React essaie déjà de combiner les mises à jour d’état dans plusieurs situations simples :
const Component = () => { const [query, setQuery] = useState(""); const [queryCount, setQueryCount] = useState(""); /** * Two state updates, only one re-render */ setQuery("demo"); setQueryCount(queryCount + 1); };
Cependant, il existe plusieurs situations où cela ne fonctionne pas. À partir de React 18, le traitement par lots s’applique à tous mises à jour, d’où qu’elles viennent. Les mises à jour qui proviennent des délais d’expiration, des promesses et des gestionnaires d’événements du navigateur seront entièrement regroupées de la même manière que le code qui se trouve directement dans votre composant.
Cette modification peut modifier le comportement de certains codes. Si vous avez un ancien composant qui met à jour l’état plusieurs fois aux endroits répertoriés ci-dessus, puis vérifie les valeurs à mi-parcours, vous constaterez peut-être qu’elles ne correspondent pas à ce que vous attendez dans React 18. flushSync
est disponible pour forcer manuellement une mise à jour d’état à valider, vous permettant de désactiver le traitement par lots.
const Component = () => { const [query, setQuery] = useState(""); const [queryCount, setQueryCount] = useState(""); const handleSearch = query => { fetch(query).then(() => { /** * Force commit and update the DOM */ flushSync(() => setQuery(query)); setQueryCount(1); }); } };
Modifications du rendu côté serveur
Le rendu côté serveur a été fortement révisé. La nouvelle fonctionnalité principale est la prise en charge du rendu en streaming, où le nouveau code HTML peut être diffusé du serveur vers votre client React. Cela vous permet d’utiliser les composants Suspense côté serveur.
Suite à ce changement, plusieurs API ont été obsolètes ou retravaillées, notamment renderToNodeStream()
. Vous devez maintenant utiliser renderToPipeableStream()
ou renderToReadableStream()
pour fournir un contenu côté serveur compatible avec les environnements de streaming modernes.
L’hydratation côté client du contenu rendu par le serveur a également été modifiée pour s’aligner sur la nouvelle API de rendu simultané. Si vous utilisez le rendu serveur et le mode simultané, remplacez hydrate()
avec hydrateRoot()
:
// OLD import {hydrate} from "react-dom"; hydrate(<App />, document.getElementById("root")); // NEW import {hydrateRoot} from "react-dom/client"; hydrateRoot(document.getElementById("root"), <App />);
Les effets du rendu en continu rendent le rendu du serveur plus adapté à des cas d’utilisation variés. L’implémentation existante de React côté serveur nécessite que le client récupère et hydrate l’intégralité de l’application avant qu’elle ne devienne interactive. En ajoutant des flux et Suspense, React peut récupérer uniquement les bits nécessaires au rendu initial, puis charger des données non essentielles supplémentaires à partir du serveur une fois l’application devenue interactive.
Conclusion
React 18 apporte de nouvelles fonctionnalités et des améliorations de performances pour vos applications. Des fonctionnalités telles que Suspense et Transitions facilitent l’écriture de plusieurs types de code et ont moins d’impact sur d’autres zones de votre application.
La mise à niveau vers React 18 lors de sa sortie devrait être assez simple dans la plupart des cas. Vous pouvez continuer à utiliser l’API racine de React 17 pour le moment avant de passer à createRoot()
lorsque vous êtes prêt à adopter le rendu simultané. Si vous souhaitez commencer à préparer votre application dès aujourd’hui, vous pouvez installer la dernière release candidate en exécutant npm install react@rc react-dom@rc
.