Les notes de mise à jour pour les nouvelles versions majeures de React sont toujours incroyablement denses en informations, et le cadre lui-même est si large que je me retrouve toujours à prendre des notes et à comparer la documentation de référence pour essayer non seulement découvrez ce qui a changé et comment l'utiliser, mais aussi comment les différents concepts majeurs dont parlent les notes de mise à jour ont réellement fonctionné sous le capot. Bien que React soit extrêmement bien documenté, les notes de mise à jour ne sont pas toujours « suffisantes » pour comprendre les concepts - par exemple, dans les notes de mise à jour de React 19, j'ai trouvé l'exemple de useOptimistic déroutant et en fait un peu inexact quant au fonctionnement réel du hook.
J'ai décidé d'écrire les notes que j'ai prises en passant en revue les modifications de React 19 pour en faire un compagnon pour tous ceux qui redoutent de parcourir les notes et de comprendre ce qu'elles signifient toutes - alors commençons.
Actions et gestion des formulaires/useActionState
La première amélioration majeure a été d'énormes améliorations dans la gestion des formulaires. React a progressivement évolué pour supprimer le passe-partout et mieux s'intégrer aux formulaires HTML natifs, et son nouveau useActionState fait partie de cette poussée. Il est destiné à aider à améliorer la gestion des erreurs (car il est désormais intégré), il suit automatiquement les états de chargement afin que vous n'ayez pas à coder manuellement en permanence les états en attente et améliore la prise en charge de l'amélioration progressive.
Maintenant, j'ai trouvé l'exemple de code fourni pour useActionState assez déroutant, et en fait, cet exemple de code est la seule raison pour laquelle j'ai commencé à écrire cet article - il y a, en fait, deux parties à useActionState, qu'ils ont combinées dans le exemple pour économiser de l'espace, mais a fini par réduire considérablement la clarté. useActionState renvoie un tuple qui suit le moment où l'action asynchrone dans la soumission du formulaire est terminée, tandis qu'une version modifiée de l'action que vous lui transmettez est directement transmise au formulaire. useActionState lui-même prend deux entrées - une formAction asynchrone (qui reçoit à la fois l'état précédent et les données du formulaire comme arguments lorsqu'il est appelé) et l'état initial - qui peut être nul, zéro ou une variable.
Si votre formulaire n'a pas encore été soumis, l'état précédent est l'état initial. Si le formulaire a déjà été soumis, il s'agit de ce qui a été soumis - dans une fonction serveur, vous pouvez en fait afficher la réponse du serveur avant même que l'hydratation ne se produise. useActionState peut enfin accepter une chaîne avec une URL de page unique que le formulaire est destiné à modifier. En d'autres termes, il peut également accepter un paramètre de permalien facultatif, particulièrement utile pour une amélioration progressive : il indique au navigateur où naviguer si un utilisateur soumet le formulaire avant le chargement de JavaScript.
Enfin, le tuple renvoyé par useActionState est un tableau composé de l'état actuel (lors de votre rendu initial, c'est votre valeur initialState), du formAction modifié par réaction que vous pouvez transmettre à un formulaire en tant qu'action ou d'un bouton en tant qu'accessoire, et un indicateur isPending/variable avec état. J'ai hâte de voir quels autres nouveaux développements l'équipe React proposera, car celui-ci semble particulièrement utile.
Mises à jour de React-DOM
Cet ajout de Reac sera familier à tous ceux qui ont utilisé NextJS et les actions de formulaire, et il semble que l'équipe Reac ait décidé que les actions de formulaire étaient prêtes à être diffusées aux heures de grande écoute. Pour tous ceux qui ne les ont pas utilisés dans NextJS ou dans un autre framework en plus de React, ils constituent essentiellement un moyen d'améliorer les performances de React en utilisant la soumission de formulaires natifs. Au lieu de onClick, vous pouvez transmettre des soumissions de formulaires natifs via la prop action - toute fonction passée à action ou formAction verra sa soumission gérée automatiquement. React réinitialisera également automatiquement tous les champs de formulaire non contrôlés. Vous disposez également d'options manuelles pour le réinitialiser via l'API. L'équipe React a également intégré la gestion des erreurs avec des limites d'erreur. Je n'en parlerai pas trop car je suppose que la plupart des gens s'en souviennent depuis NextJS, mais je peux écrire un suivi si quelqu'un a des questions.
hook useFormStatus
C'est un excellent ajout pour vous aider à voir ce qui se passe dans votre formulaire sans perçage d'accessoires ni utilisation de contexte - si vous demandez pourquoi ne pas percer d'accessoires, il s'agit de garder le code maintenable et facile à modifier. En ce qui concerne le contexte, une utilisation excessive du contexte entraînera des problèmes de performances, car chaque composant abonné à un contexte particulier sera restitué chaque fois que quelque chose dans le contexte change. Ainsi, cela désencombre le code, réduit les risques d’erreurs et vous empêche de gâcher les performances de votre application.
Le hook renvoie un objet avec quatre propriétés : ending - un booléen qui indique s'il y a une soumission en attente, data - un objet formData avec les données soumises par le formulaire parent (cela est nul s'il n'y a pas de soumission active ou de formulaire parent ), la méthode (get ou post) et l'action - qui est l'action transmise via le accessoire d'action.
utiliser le crochet optimiste
La nouvelle façon plus simple de gérer les mises à jour optimistes. Si vous n'êtes pas familier avec les mises à jour optimistes, cela signifie mettre à jour l'affichage côté client avant que les mises à jour côté serveur n'aient lieu. Si vous avez déjà aimé quelque chose, vu l'animation jouer et l'avoir enregistrée sur votre écran comme vous l'aviez aimé, puis reçu une erreur de toast indiquant « comme un échec », cela est dû à un rendu optimiste.
Le hook useOptimistic accepte une variable avec état dont vous souhaitez un rendu optimiste et une fonction de mise à jour, qui doit être une fonction pure - en d'autres termes, une fonction déterministe sans effets secondaires. Fondamentalement, la fonction de mise à jour récupère la source de la mise à jour vers l'état - donc généralement quelque chose comme formData.get('name'). useOptimistic renvoie alors deux valeurs : l'état optimiste et une fonction addOptimistic.
J'ai trouvé la documentation à ce sujet un peu faible, en particulier en ce qui concerne le flux d'utilisation - en gros, vous appelez useOptimistic et lui transmettez l'état initial pour lequel vous souhaitez afficher les mises à jour optimistes et une fonction de mise à jour. Vous recevez deux fonctions : la nouvelle valeur avec état améliorée et optimiste (optimisticState) et une fonction permettant de changer d'état de manière optimiste. Lorsque vous avez une nouvelle valeur soumise par l'utilisateur, vous appelez la deuxième fonction, appelée addOptimistic dans la documentation, et lui transmettez la valeur soumise par l'utilisateur. Dans votre composant, vous lui transmettez ensuite la valeur avec état améliorée et optimiste chaque fois que vous souhaitez restituer la variable avec état de manière optimiste.
Dans l'ensemble, j'aime vraiment cette manière plus standardisée d'effectuer des mises à jour optimistes - j'ai déjà eu des problèmes avec la mise en cache dans NextJS et la réalisation de mises à jour optimistes, donc une manière standardisée de créer des mises à jour optimistes est excellente, et je suis sûr que ce sera le cas. passez à NextJS, si ce n'est pas déjà fait.
L'API d'utilisation
Il s'agit d'une API super dense, et c'est une toute nouvelle façon d'accéder aux ressources pendant que React rend une page - l'expression exacte utilisée est « lire les ressources lors du rendu ». Alors à quoi ça sert concrètement ? Cette nouvelle API peut être utilisée pour accéder aux informations sur les composants à l'intérieur de conditions ou de boucles. Si vous ne savez pas pourquoi cela est utile, cela a à voir avec le fonctionnement du processus de rendu de React. React/react-fiber repose sur le rendu de tout dans le même ordre à chaque fois, c'est pourquoi vous ne pouvez généralement pas accéder à la plupart des hooks pendant le processus de rendu. Pour le dire plus clairement, l'état est en fait suivi en fonction de l'ordre dans lequel les hooks sont appelés, donc si les hooks sont appelés dans un ordre imprévisible, vous vous retrouvez avec des bugs de rendu. Un bon exemple est l'accès à un contexte de thème selon que l'utilisateur est connecté ou non.
Alors pourquoi est-ce une évolution importante ? Cela signifie que vous pouvez charger des informations/données uniquement lorsque cela est réellement nécessaire, par ex. ce n'est que si un utilisateur est réellement connecté que vous chargerez du CSS pour un thème spécial. Les données doivent être sérialisables, ce qui signifie que vous pouvez envoyer une promesse du composant serveur à un composant client alors qu'il est en fait déjà en cours - cela signifie qu'il y a moins de requêtes en cascade et qu'elles sont automatiquement mises en parallèle. Il convient de noter que lorsque vous travaillez dans des composants serveur, vous devriez en fait préférer utiliser async/await plutôt que l'utilisation pour la récupération de données - en effet, async/await reprendra le rendu exactement là où il s'était arrêté, tandis que l'utilisation déclenchera un nouveau rendu complet de le composant après la résolution des données. Bien sûr, je tiens également à noter que ce changement signifie également que vous disposez d'une nouvelle source potentielle de requêtes en cascade si vous configurez l'utilisation de manière incorrecte.
Une chose très importante à noter est que vous ne pouvez pas utiliser l'API « use » dans un bloc try/catch - en effet, « use » utilise automatiquement les limites de suspense de réaction lorsqu'il est appelé avec une promesse. Un bloc try/catch vous empêche d'atteindre le niveau de réaction, car toute erreur arrêterait en fait l'exécution au niveau JS avant que vous n'atteigniez React, brisant ainsi la fonctionnalité. Comme les autres hooks, ils doivent se trouver au niveau supérieur de portée d'un composant ou d'une fonction particulière (encore une fois, en raison de l'ordre de rendu).
Donc, quel est le véritable objectif de « utilisation » est de vous aider à accéder au contexte pour rendre les choses de manière conditionnelle et à récupérer les données uniquement lorsque cela est réellement nécessaire. C'est encore une autre étape vers une réaction un peu moins mystérieuse, en simplifiant la récupération de données conditionnelles, en améliorant l'expérience de développement et en améliorant les performances d'un seul coup. Cela nécessite des développeurs React plus expérimentés pour réapprendre à faire des choses comme la création de thèmes, mais je pense que cela rend le framework beaucoup plus accessible aux nouveaux utilisateurs, ce qui est toujours génial.
Nouvelles API statiques React Dom
Ces deux nouvelles API statiques, prerender et prerenderToNodeStream sont toutes deux des améliorations de renderToString, qui est utilisé pour le rendu côté serveur (SSR). Ces nouvelles améliorations concernent la génération de sites statiques (SSG) à l'aide de renderToString. Contrairement au streaming SSR traditionnel qui peut envoyer des morceaux de contenu dès qu'ils deviennent disponibles, ces nouvelles API attendent spécifiquement que TOUTES les données soient chargées avant de générer le HTML final. Ils sont conçus pour fonctionner de manière transparente avec les environnements de streaming modernes tels que Node.js Streams et Web Streams, mais ils ne prennent pas intentionnellement en charge le streaming de contenu partiel. Au lieu de cela, ils garantissent que vous obtenez des pages complètes et entièrement chargées au moment de la construction. Elles diffèrent des méthodes traditionnelles de streaming SSR, qui envoient des sites pré-rendus dès que les données deviennent disponibles.
Nous avions déjà des frameworks compatibles SSG construits sur React, comme NextJS, mais il s'agit de la fonctionnalité native de React pour SSG. Les frameworks dans lesquels SSG utilisait renderToString, puis construisaient leurs propres coordinations complexes de récupération de données autour de celui-ci. Il était extrêmement difficile pour les utilisateurs de le créer eux-mêmes, et pour ce faire, ces frameworks utilisaient des pipelines extrêmement complexes. Ces nouvelles méthodes permettent essentiellement de charger les données pendant la génération HTML statique. Si vous n'êtes pas familier avec SSG, il s'agit essentiellement de tout restituer au moment de la construction et constitue sans aucun doute la méthode la plus rapide de rendu des pages, car elle n'a pas pour rendre les pages à la demande de l'utilisateur, c'est donc formidable d'avoir ce type de fonctionnalité pour les personnes qui ne veulent pas utiliser quelque chose comme Next, qui nécessite soit un déploiement sur Vercel, soit un processus de déploiement extrêmement complexe.
Composants du serveur
Le concept de composants serveur React ne sera pas nouveau pour quiconque a utilisé une version récente de NextJS, mais pour tous ceux qui ne l'ont pas encore fait, les composants serveur constituent un nouveau paradigme autour de la récupération de données. Franchement, le concept de composants serveur (et d'actions serveur) mérite à lui seul un article entier, mais pour résumer brièvement le concept, les composants serveur sont toujours rendus sur le serveur avant d'être envoyés au client, même après le chargement de javascript. Cela signifie que les rendus suivants sont effectués côté serveur.
Le principal avantage de ces composants est la sécurité et la récupération de données : si vous demandez des données à l'intérieur d'un composant serveur, les informations de la demande n'apparaissent jamais côté client, uniquement la réponse, ce qui la rend beaucoup plus sécurisée. Les API, les points de terminaison, etc. ne sont tout simplement pas accessibles côté client. Cela réduit également la taille du bundle puisque le javascript pour ces actions n'est jamais envoyé au client. Il vous permet en outre d'exécuter des opérations gourmandes en mémoire ou en calcul sur le serveur afin de réduire la charge de rendu sur des machines moins puissantes. Ils réduisent également les cascades côté client puisque la récupération séquentielle des données peut être effectuée sur des machines plus proches de vos bases de données, mais bien sûr, cela ouvre également la possibilité de toutes nouvelles cascades côté serveur puisque vos développeurs perdent l'accès aux demandes d'informations faciles auprès du développeur du navigateur de test. outils et doivent utiliser quelque chose comme un collecteur et une visionneuse OpenTelemetry pour les examiner. Enfin, les composants du serveur sont également parfaits pour une amélioration progressive.
Les composants du serveur sont également accompagnés d'une liste de limitations : vous ne pouvez pas utiliser les API du navigateur local (stockage local, fenêtre, etc.), les hooks de réaction fonctionneront différemment, vous ne pouvez pas compter sur ou utiliser l'état, et vous pouvez ' Je n'utilise pas de gestionnaires d'événements, l'interactivité des utilisateurs pour les composants est donc décidément mince. Fondamentalement, considérez ce paradigme comme la récupération de données dans les composants serveur, l'interactivité sur les composants clients.
La mise en garde la plus importante pour toute personne qui découvre les composants serveur est que vous ne pouvez pas les importer dans un composant client - si vous le faites, cela entraînera une erreur (en supposant que vous ayez ajouté une récupération de données) car cela amènera le compilateur à traiter ledit composant serveur en tant que composant client. Si vous souhaitez transmettre un composant serveur à un composant client, vous devez le transmettre via la prop {children}.
Actions du serveur
Il s'agit d'un autre sujet complexe avec de nombreuses implications sur la façon dont vous créez vos produits et fonctionnalités, et ceux-ci sont également présents dans NextJS depuis environ un an. Les actions du serveur sont déclarées en tapant « utiliser le serveur » en haut d'un fichier et transmettent une référence directe à ladite fonction serveur qui peut ensuite être appelée depuis l'intérieur d'un composant client.
À un certain niveau, ceux-ci sont conceptuellement similaires aux appels de procédure à distance (RPC) : ils vous permettent tous deux d'appeler des fonctions côté serveur à partir du client, tous deux résument la complexité des interactions client-serveur et gèrent tous deux la sérialisation et la désérialisation, mais il y a quelques différences clés à prendre en compte. Le principal avantage des actions de serveur est qu'elles fonctionnent avec l'amélioration progressive de React, aident à appliquer la sécurité des types au-delà des limites client/serveur, ont des optimisations de performances intégrées (liées à l'amélioration progressive) et ont une intégration plus transparente avec l'écosystème React. en ce sens qu'ils fournissent des états en attente intégrés et sont automatiquement intégrés aux soumissions de formulaires natifs.
Lorsque vous parlez de RPC moderne, quelque chose comme gRPC qui dispose déjà d'une sécurité de type et de certaines optimisations de performances, le principal avantage des actions du serveur se résume généralement à la gestion intégrée des formulaires et à l'amélioration progressive, mais surtout, cela fonctionne également. avec des limites de suspense et d'erreur de réaction. Plus important encore, le déploiement est beaucoup plus simple puisque vous n'avez pas nécessairement besoin de configurer un serveur séparé pour gRPC, ils sont donc absolument plus idéaux pour les petits projets, bien que lorsqu'il s'agit de projets plus importants, je peux voir que gRPC est beaucoup plus souhaitable puisque cela vous donne plus de flexibilité en termes de langage backend, etc.
Contexte en tant que fournisseur
Il s'agit essentiellement d'une simplification de la syntaxe, aidant React en général à avoir une syntaxe déclarative beaucoup plus propre. Pour être honnête, je n’ai pas grand-chose à dire à ce sujet à part « j’aime ça ».
Réf comme fonction d'accessoire et de nettoyage pour les références
Auparavant, pour nettoyer les références, vous deviez effectuer une vérification nulle à l'intérieur de votre composant, et la référence était nettoyée à un moment quelque peu indéterminé. React appellerait votre rappel ref avec null lorsque le composant serait démonté, ce qui vous obligerait à gérer explicitement les cas d'attachement et de détachement. L'avantage de la nouvelle syntaxe ref est le nettoyage déterministe - ce qui signifie qu'il est désormais beaucoup plus facile de travailler avec des ressources externes et des bibliothèques tierces car vous saurez exactement quand le nettoyage des références aura lieu (au démontage). Avec la nouvelle approche, vous pouvez renvoyer directement une fonction de nettoyage. L'intégration TypeScript nécessite des instructions de retour explicites pour éviter toute ambiguïté sur les fonctions de nettoyage.
J'aime vraiment la façon dont cela a été implémenté car c'est le même modèle que useEffect - maintenir la cohérence à travers le framework est génial. Je pense que ce nouveau nettoyage de référence sera spécifiquement très utile pour les contextes WebGL et autres ressources lourdes, mais aussi pour gérer les écouteurs d'événements DOM ajoutés à l'aide de méthodes JS natives. En effet, auparavant, réagir supprimait la référence à l'élément dom lors du nettoyage… Mais si vous avez fait cela, il devient beaucoup plus complexe de supprimer les écouteurs d'événements puisque vous avez perdu la référence au composant auquel ils sont attachés. . En conséquence, vous deviez stocker vos références en dehors de l'élément, ce qui ajoutait une couche de complexité. Vous pouvez désormais simplement supprimer les écouteurs d'événements dans la fonction de retour de votre composant, car vous conservez l'accès à la référence dans ladite fonction de retour. Cela signifie également que nous n'avons plus à nous soucier du cas nul dans la plupart des situations, car React n'appellera plus la référence avec null lors de l'utilisation des fonctions de nettoyage. La fonction de nettoyage elle-même remplace cette fonctionnalité, rendant notre code plus propre et plus prévisible.
useDeferredValue
Le but du hook useDeferredValue est de gérer des opérations coûteuses en termes de calcul tout en conservant une interface utilisateur réactive. Il y parvient en permettant aux composants d'afficher une valeur « périmée » précédente lors du calcul ou de la récupération de nouvelles données en arrière-plan. Un cas d'utilisation courant est la fonctionnalité de recherche qui affiche les résultats au fur et à mesure que les utilisateurs tapent - sans différer, chaque frappe pourrait déclencher une opération de recherche coûteuse qui rendrait la saisie lente. Grâce à useDeferredValue, l'interface peut rester réactive en affichant les résultats de recherche précédents tout en en calculant de nouveaux.
Ce nouvel ajout est une amélioration importante du comportement de chargement initial de ce crochet. Auparavant, le premier rendu utilisait immédiatement la valeur transmise à useDeferredValue, déclenchant potentiellement un calcul coûteux dès le début. La nouvelle version vous permet de fournir une valeur initiale légère (telle qu'une chaîne vide) qui s'affiche immédiatement, tout en traitant la valeur la plus coûteuse en arrière-plan. Cela signifie que vous pouvez montrer aux utilisateurs un retour immédiat avec une valeur par défaut sûre, puis le mettre à jour avec des données réelles une fois le calcul coûteux terminé. Essentiellement, cela rend useDeferredValue encore meilleur pour l’amélioration des performances.
Ajouts de métadonnées de documents
Ces nouvelles modifications concernent l'ajout de diverses balises de métadonnées à l'intérieur des composants réels. Ils examinent trois options : ,
Le principal avantage de ces changements est qu'ils permettent aux composants d'être véritablement autonomes : ils peuvent désormais gérer leurs propres métadonnées ainsi que leurs styles et scripts. Vous n’avez plus besoin de comprendre comment élever les métadonnées au niveau supérieur ou d’utiliser une bibliothèque pour le faire, ce qui rend le référencement beaucoup plus facile à réaliser. Différentes pages d'un SPA peuvent avoir différentes métadonnées plus facilement sans risquer de désynchroniser les métadonnées avec le contenu affiché sur la page, ce qui était auparavant un risque lorsqu'il y avait différentes métadonnées pour différentes pages.
Support des feuilles de style
Les gens utilisent peut-être Tailwind depuis si longtemps qu'ils l'ont oublié, mais l'exécution de CSS non encapsulés peut créer des problèmes en raison de la façon dont les feuilles de style sont chargées, à savoir les règles de priorité de l'ordre de chargement. Premièrement, vous pouvez vous retrouver avec des flashs de contenu sans style : chaque fois que le code HTML se charge et s'affiche avant la fin du téléchargement du CSS, il s'affiche sous la forme d'une page entièrement blanche, généralement dans une seule colonne avec des tailles de police massives et des tailles d'image natives, ce qui est souvent le cas. le rend illisible.
De plus, les règles d'ordre de chargement CSS peuvent créer d'autres problèmes : par exemple, si vous avez des règles avec la même spécificité mais que vous vous attendez à ce qu'elles se chargent dans un certain ordre. Ceci est pertinent, par exemple, lorsque vous disposez de différentes options pour les thèmes (par exemple le mode sombre). Si le CSS du mode sombre est destiné à être chargé en dernier, mais que votre fichier CSS pour le mode sombre se charge en premier, vous pouvez vous retrouver avec le mode sombre étant clair, ou certaines parties de l'application où les règles sont respectées et d'autres parties où elles le sont. t respecté.
Il existait de nombreuses solutions pour éviter cela, comme CSS dans JS, charger tous les CSS dans le répertoire
balise, temps de construction regroupant les CSS, etc. Cependant, ces nouvelles modifications CSS sont destinées à aider à gérer ces problèmes en définissant la priorité de manière déclarative - elles garantissent également que toutes les feuilles de style placées dans un composant sont chargées avant le rendu du composant lui-même. Il est également intégré à la déduplication, de sorte que vous n'avez pas à charger la même feuille de style plusieurs fois lorsque vous réutilisez des composants avec des liens de feuille de style inclus.La façon dont vous accédez à cette nouvelle fonctionnalité (attendre que le CSS se charge avant de restituer un composant, hisser automatiquement le CSS en tête, etc.) est également assez simple - il vous suffit d'inclure une priorité dans un Prise en charge des scripts asynchrones Cela répond à un cas limite, par opposition à la création d'une nouvelle fonctionnalité de base dont les gens doivent s'inquiéter : ajouter manuellement/gérer directement des scripts avec un