Maison > interface Web > tutoriel CSS > React suspense: leçons apprises lors du chargement des données

React suspense: leçons apprises lors du chargement des données

尊渡假赌尊渡假赌尊渡假赌
Libérer: 2025-03-18 10:18:15
original
985 Les gens l'ont consulté

React suspense: leçons tirées du chargement des données

React suspense: leçons apprises lors du chargement des données

Le suspense est une fonctionnalité à venir de React qui aide à coordonner les opérations asynchrones telles que le chargement des données, ce qui vous permet d'empêcher facilement les incohérences de l'État dans l'interface utilisateur. Je vais expliquer plus en détail ce que cela signifie exactement et donner une brève introduction au suspense, puis un cas d'utilisation plus réaliste et quelques leçons apprises.

Les fonctions que j'ai introduites sont toujours au stade alpha et ne doivent pas être utilisées dans des environnements de production. Ce message est pour ceux qui veulent avoir le premier aperçu des fonctionnalités à venir et comprendre où ils se dirigeront vers le futur.

Commencer avec le suspense

L'une des parties les plus difficiles du développement d'applications est la coordination de l'état d'application et la façon dont les données sont chargées. Les modifications d'état déclenchent généralement de nouveaux données de données à plusieurs emplacements. En règle générale, chaque élément de données aura sa propre interface utilisateur de chargement (comme un "rotateur") qui est à peu près là où les données se trouvent dans l'application. La nature asynchrone du chargement des données signifie que ces demandes peuvent être renvoyées dans n'importe quel ordre. En conséquence, non seulement votre application apparaîtra et disparaîtra avec de nombreux filateurs différents, mais pire, votre application peut afficher des données incohérentes. Si deux de vos trois charges de données sont terminées, vous verrez un spinner de charge en haut de la troisième position, montrant toujours les données anciennes, désormais obsolètes.

Je sais que c'est trop. Si vous trouvez un de cela déroutant, vous pourriez être intéressé par un article précédent sur le suspense que j'ai écrit. Cet article présente plus en détail ce qu'est le suspense et ce qu'il implémente. Notez que certains de ces petits détails sont désormais obsolètes, c'est-à-dire que le crochet useTransition n'accepte plus les valeurs timeoutMs , mais attend indéfiniment.

Maintenant, jetons un coup d'œil aux détails, puis passons à un cas d'utilisation spécifique avec des pièges cachés.

Comment fonctionne le suspense

Heureusement, l'équipe React est suffisamment intelligente pour ne pas limiter ces efforts pour charger des données. Le suspense fonctionne à travers des primitives de bas niveau, que vous pouvez appliquer à presque n'importe quoi. Jetons un coup d'œil à ces primitives.

Tout d'abord<suspense></suspense> Boundaire, il accepte un attribut fallback :

<suspense fallback="{<Fallback"></suspense> }>
Copier après la connexion
Copier après la connexion

Chaque fois que des composants d'enfants sous ce composant sont suspendus, il rend fallback . Peu importe le nombre de sous-composants suspendus, pour une raison quelconque, fallback est affiché. C'est une façon dont React garantit que l'interface utilisateur est cohérente - elle ne rendra rien tant que tout n'est pas prêt.

Mais que se passe-t-il lorsque l'utilisateur modifie l'état et charge de nouvelles données une fois le contenu initialement rendu? Nous ne voulons certainement pas que notre interface utilisateur existante disparaisse et montre notre fallback ; ce serait une mauvaise expérience utilisateur. Au lieu de cela, nous pourrions vouloir afficher un spinner de chargement jusqu'à ce que toutes les données soient prêtes avant que la nouvelle interface utilisateur ne soit affichée.

Le crochet useTransition implémente cela. Ce crochet renvoie une fonction et une valeur booléenne. Nous appelons la fonction et enroulons nos changements d'état. Maintenant, les choses deviennent intéressantes. React essaie d'appliquer nos modifications de statut. Si quelque chose est suspendu, React définit la valeur booléenne à true et attend que le hang se termine. Une fois terminé, il essaiera d'appliquer à nouveau le changement d'état. Peut-être qu'il réussira cette fois, ou peut-être que quelque chose d'autre se bloquera. Quoi qu'il en soit, le drapeau booléen restera true jusqu'à ce que tout soit prêt, et ce n'est qu'alors que le changement d'État sera terminé et reflété dans l'interface utilisateur.

Enfin, comment pouvons -nous nous accrocher? Nous nous accrochons en lançant une promesse. Si les données sont demandées et que nous devons récupérer, nous allons aller - et lancer une promesse liée à cette récupération. Ce mécanisme de suspension de bas niveau signifie que nous pouvons l'utiliser pour n'importe quoi. L'utilitaire React.lazy pour les composants de chargement paresseux fonctionne déjà avec le suspense, et j'ai écrit avant d'utiliser le suspense pour attendre que l'image se charge avant d'afficher l'interface utilisateur pour empêcher le contenu de se déplacer.

Ne vous inquiétez pas, nous discuterons de tout cela.

Que construisons-nous

Nous allons construire quelque chose de légèrement différent des exemples d'autres articles similaires. N'oubliez pas que le suspense est toujours en phase alpha, donc votre utilité de données de chargement préférée peut ne pas encore avoir de support de suspense. Mais cela ne signifie pas que nous ne pouvons pas simuler quelque chose et comprendre comment fonctionne le suspense.

Créons une liste de chargement infinie qui affiche certaines données et combine certaines images préchargées en fonction du suspense. Nous afficherons nos données, ainsi qu'un bouton pour charger plus de données. Lorsque les données sont rendues, nous préchargerons l'image associée et la pension avant qu'elle ne soit prête.

Ce cas d'utilisation est basé sur le travail réel que j'ai fait dans mon projet secondaire (encore une fois, n'utilisez pas de suspense en production mais le projet latéral est autorisé). J'utilisais mon propre client GraphQL à l'époque et la motivation de ce post était quelques-unes des difficultés que j'avais. Pour simplifier les opérations et nous concentrer sur le suspense lui-même, plutôt que sur tout utilitaire de chargement de données, nous allons simplement forger le chargement des données.

Commencez à construire!

C'est le bac à sable que nous avons initialement essayé. Nous allons l'utiliser pour tout expliquer étape par étape, il n'est donc pas nécessaire de se précipiter pour comprendre tout le code maintenant.

Notre composant App racine rend une limite de suspense comme celle-ci:

<suspense fallback="{<Fallback"></suspense> }>
Copier après la connexion
Copier après la connexion

fallback rend chaque fois que quelque chose est tombé en suspens (sauf si le changement d'état se produit dans l'appel useTransition ). Pour rendre les choses plus faciles à comprendre, j'ai fait ce composant Fallback à tourner l'interface utilisateur rose, il est donc difficile à manquer; notre objectif est de comprendre le suspense, plutôt que de construire une interface utilisateur de haute qualité.

Nous chargeons le bloc de données actuel à l'intérieur DataList :

 const newData = useQuery (param);
Copier après la connexion

Notre crochet useQuery est codé en dur pour renvoyer de fausses données, y compris un délai d'expiration pour les demandes de réseau simulées. Il gère les résultats mis en cache et lance une promesse si les données ne sont pas mises en cache.

Nous enregistrons l'état (au moins pour l'instant) dans la liste de données principale que nous affichons:

 const [data, setData] = useState ([]);
Copier après la connexion

Lorsque de nouvelles données sont transmises de notre crochet, nous les ajoutons à notre liste principale:

 useEFFECT (() => {
  setData ((d) => d.concat (newData));
}, [newData]);
Copier après la connexion
Copier après la connexion

Enfin, lorsque l'utilisateur a besoin de plus de données, il clique sur le bouton, qui appelle cette fonction:

 fonction chargemore () {
  startTransition (() => {
    setParam ((x) => x 1);
  });
}
Copier après la connexion

Enfin, notez que j'utilise le composant SuspenseImg pour gérer la précharge de l'image que j'affiche avec chaque pièce de données. Seules cinq images aléatoires sont affichées, mais j'ai ajouté une chaîne de requête pour m'assurer que de nouvelles charges sont faites pour chaque nouvelle pièce de données que nous rencontrons.

Résumer

Pour résumer où nous en sommes actuellement, nous avons un crochet qui charge les données actuelles. Ce crochet suit le mécanisme de suspense et lance une promesse lorsque le chargement se produit. Chaque fois que ces données changent, la liste totale des projets en cours d'exécution est mise à jour et de nouveaux projets sont joints. Cela se produit dans useEffect . Chaque projet rend une image, nous utilisons le composant SuspenseImg pour précharger l'image et accrocher avant qu'il ne soit prêt. Si vous êtes curieux de savoir comment fonctionne un code, consultez mon article précédent sur les images préchargenables avec suspense.

Testons-le

Si tout fonctionne bien, ce sera un article de blog très ennuyeux, ne vous inquiétez pas, ce n'est pas normal. Notez que l'écran fallback rose sera affiché puis rapidement caché à la charge initiale, mais redevient ensuite.

Lorsque nous cliquons sur le bouton pour charger plus de données, nous voyons l'indicateur de charge en ligne (contrôlé par useTransition ) se retourner sur true . Ensuite, nous le voyons se retourner à false et nos affichages fallback roses d'origine. Nous nous attendons à ce que l'écran rose ne voit plus après la charge initiale; l'indicateur de charge en ligne doit être affiché jusqu'à ce que tout soit prêt. ce qui s'est passé?

question

Il a été caché dans un endroit visible:

 useEFFECT (() => {
  setData ((d) => d.concat (newData));
}, [newData]);
Copier après la connexion
Copier après la connexion

useEffect s'exécute lorsque la modification de l'état est terminée, c'est-à-dire que le changement d'état a été pendu et appliqué au DOM. Cette partie, "terminée en attente" est la clé ici. Nous pouvons définir l'État ici si nous le voulons, mais si ce changement d'état se bloque à nouveau, c'est un tout nouveau hang. C'est pourquoi nous voyons le clignotement rose après la charge initiale et le chargement des données ultérieur. Dans les deux cas, le chargement des données est effectué, puis nous définissons l'état dans un seul effet, ce qui fait que les nouvelles données se rendent et s'accrochent à nouveau parce que l'image est préchargée.

Alors, comment résolvons-nous ce problème? À un niveau, la solution est simple: arrêtez de régler l'état dans l'effet. Mais cela est plus facile à dire qu'à faire. Comment pouvons-nous mettre à jour la liste des entrées en cours d'exécution pour joindre de nouveaux résultats sans utiliser d'effets? Vous pourriez penser que nous pouvons utiliser Ref pour suivre les choses.

Malheureusement, le suspense apporte de nouvelles règles sur les références, c'est-à-dire que nous ne pouvons pas définir des références à l'intérieur du rendu. Si vous vous demandez pourquoi, n'oubliez pas que le suspense consiste à réagir en essayant de gérer le rendu, en voyant la promesse jetée, puis en jetant ce rendu à mi-chemin. Si nous modifions l'arbitre avant que le rendu ne soit annulé et jeté, l'arbitre a toujours ce changement, mais a une valeur non valide. La fonction de rendu doit être pure et n'a aucun effet secondaire. Cela a toujours été une règle pour réagir, mais maintenant c'est plus important.

Repenser notre chargement de données

C'est la solution, nous expliquerons son paragraphe par paragraphe.

Tout d'abord, au lieu de stocker notre liste de données principale à l'état, faisons quelque chose de différent: stockons la liste des pages que nous visons. Nous pouvons stocker la page la plus récente de la référence (bien que nous ne l'écrivons pas dans le rendu) et stocker un tableau de toutes les pages actuellement chargées dans l'État.

 const CurrentPage = useRef (0);
const [pages, setPages] = useState ([currentPage.current]);
Copier après la connexion

Pour charger plus de données, nous mettrons à jour en conséquence:

 fonction chargemore () {
  startTransition (() => {
    currentPage.current = currentPage.current 1;
    setPages ((pages) => pages.concat (currentPage.current));
  });
}
Copier après la connexion

Cependant, la partie délicate est de savoir comment convertir ces numéros de page en données réelles. Ce que nous ne pouvons pas faire, c'est faire une boucle à travers ces pages et appeler notre crochet useQuery ; Nous avons besoin d'une nouvelle API de données non basée sur le hook. Selon la convention très non officielle que j'ai vue dans mes démos de suspense passées, j'ai nommé cette méthode read() . Ce ne sera pas un crochet. Si les données sont mises en cache, elle renvoie les données demandées, sinon une promesse sera lancée. Pour notre faux crochet de chargement de données, il n'était pas nécessaire d'apporter de réels changements; Mais pour la bibliothèque réelle des services de chargement des données, il peut prendre un travail à l'auteur pour exposer les deux options dans le cadre de son API publique. Dans le client GraphQL que j'ai mentionné plus tôt, il y a en effet à la fois le crochet useSuspenseQuery et read() sur l'objet client.

Avec cette nouvelle méthode read() , la dernière partie de notre code est triviale:

 const data = pages.flatMap ((page) => read (page));
Copier après la connexion

Nous prenons chaque page et demandons les données correspondantes à l'aide de notre méthode read() . Si une page n'est pas mise en cache (ce ne devrait être que la dernière page de la liste), une promesse sera lancée et réagira pour nous. Lorsque Promise Parses, React essaiera à nouveau le changement d'état précédent, et ce code sera à nouveau exécuté.

Ne laissez pas flatMap Call vous confondre. C'est exactement la même chose que ce que fait map , sauf qu'il obtient chaque résultat dans le nouveau tableau et "s'aplatit" s'il s'agit en soi un tableau.

résultat

Avec ces changements, lorsque nous commençons, tout fonctionne comme prévu. Notre écran de chargement rose s'affiche une fois sur la charge initiale, puis dans le chargement ultérieur, l'état de chargement en ligne s'affiche jusqu'à ce que tout soit prêt.

Conclusion

Le suspense est une mise à jour passionnante à réagir. Il est toujours en phase alpha, alors n'essayez pas de l'utiliser pour quelque chose d'important. Cependant, si vous êtes le type de développeur qui aime obtenir le premier à découvrir le contenu à venir, j'espère que cet article vous fournira des antécédents et des informations utiles qui seront utiles lors de la publication.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal