Implémenter des écrans squelettes dans React
Publié: 2022-03-10Les spinners et les chargeurs ont traditionnellement été le moyen de dire aux utilisateurs que le contenu va prendre un certain temps à se charger. Bien que cette approche soit excellente, elle devient rapidement obsolète dans le développement moderne. Les écrans squelettes deviennent le remplacement parfait des chargeurs traditionnels car ils se concentrent sur la progression plutôt que sur les temps d'attente, réduisant ainsi la frustration liée au temps de chargement.
Dans cet article, nous n'aborderons pas les bases de CSS React ou de la syntaxe JavaScript, vous n'avez donc pas besoin d'être un expert dans l'un ou l'autre de ces langages pour suivre.
Les experts en UI et UX nous enseignent que, pendant que les utilisateurs attendent que le contenu se charge sur une page, nous devons les garder engagés.
L'idée derrière l'utilisation de spinners pour engager les utilisateurs avant le chargement du contenu est excellente ; cependant, le résultat peut être loin d'être idéal car la plupart des utilisateurs s'ennuieront à regarder un spinner animé factice comme s'il s'agissait d'une horloge. Luke Wroblewski développe cela.
Les écrans squelettes offrent une meilleure expérience utilisateur en réduisant la frustration liée au temps de chargement. En se concentrant sur la progression plutôt que sur les temps d'attente, cela crée l'illusion pour les utilisateurs que les informations seront progressivement affichées à l'écran. Bill Chung dans ses recherches le confirme.
Qu'est-ce qu'un écran squelette ?
Un écran squelette est une version de l'interface utilisateur qui ne contient pas de contenu réel ; au lieu de cela, il imite la mise en page de la page en affichant ses éléments sous une forme similaire au contenu réel au fur et à mesure qu'il se charge et devient disponible (c'est-à-dire lorsque la latence du réseau le permet).
Un écran squelette est essentiellement une structure filaire de la page, avec des espaces réservés pour le texte et les images.
Qu'y a-t-il d'unique dans un écran squelette ?
Une interface utilisateur squelette ressemble à l'interface utilisateur réelle de la page, de sorte que les utilisateurs comprendront à quelle vitesse l'application Web ou mobile se chargera avant même que le contenu ne soit affiché. Voici quelques raisons pour lesquelles vous pourriez envisager d'utiliser des écrans squelettes dans votre prochain projet :
- imiter la mise en page d'une page est plus facile avec un squelette d'écran,
- le contenu se charge progressivement (pas tout à la fois).
Les écrans squelettes sont également appelés :
- éléments fantômes,
- espaces réservés de contenu,
- chargeurs de contenu.
Blockchain.com, YouTube, Facebook, Medium et d'autres grandes entreprises technologiques affichent des écrans squelettes pendant que leur contenu se charge pour booster l'UX.
Blockchain.com
Moyen
Types d'écrans squelettes
Il existe différents types d'écrans squelettes. Les principaux sont les espaces réservés de texte et les espaces réservés d'image (ou de couleur).
La plupart des développeurs préfèrent utiliser des espaces réservés de texte comme squelette d'interface utilisateur sur leurs pages, car ils sont faciles à créer et le développeur n'a besoin d'aucun détail sur la substance du contenu réel. à la place, le squelette imite l'interface utilisateur.
Les espaces réservés de couleur sont plus difficiles à créer car ils nécessitent des détails sur le contenu.
Certains packages populaires facilitent la mise en œuvre d'écrans squelettes dans les applications Web. Examinons les deux de plus près :
- Espace réservé de réaction
- Squelette de chargement réactif
Nous examinerons les avantages et les inconvénients de chaque package, avant de déterminer lequel utiliser pour notre application.
Espace réservé de réaction
Avantages
- Les composants d'espace réservé sont utilisés pour créer une interface utilisateur squelette personnalisée.
- L'animation par impulsion (c'est-à-dire l'effet de mouvement sur un élément) est prise en charge.
- Il est livré avec une API basée sur des composants.
Les inconvénients
- Les composants squelettes sont gérés séparément, donc la mise à jour des styles d'un composant nécessite éventuellement la mise à jour du composant squelette également.
- La courbe d'apprentissage n'est pas linéaire car il existe plusieurs composants pour différents besoins.
Voici un exemple de composant squelette utilisant le package react-placeholder
:
import { TextBlock, RectShape } from 'react-placeholder/lib/placeholders'; import ReactPlaceholder from 'react-placeholder'; const GhostPlaceholder = () => ( <div className='my-placeholder'> <RectShape color='gray' style={{width: 25, height: 70}} /> <TextBlock rows={6} color='blue'/> </div> ); <ReactPlaceholder ready={ready} customPlaceholder={<GhostPlaceholder />}> <MyComponent /> </ReactPlaceholder>
TextBlock
et RectShape
à partir de react react-placeholder/lib/placeholder
et ReactPlaceholder
à partir de react react-placeholder
, nous avons créé un composant fonctionnel nommé GhostPlaceholder
. GhostPlaceholder
a un div, et à l'intérieur du div, nous avons utilisé le composant RectShape, qui décrit les dimensions d'un rectangle, transmet la valeur de n'importe quelle couleur et définit les styles du rectangle.
Ensuite, nous avons utilisé le composant TextBlock
pour définir les valeurs des lignes et de la couleur. Le composant TextBlock
définit le nombre de lignes et la couleur du texte.
Nous transmettons MyComponent
en tant qu'enfant du composant ReactPlaceholder
, qui reçoit ready
et le composant GhostPlaceholder
comme valeurs pour ses props ready
et customPlaceholder
.
Le MyComponent
se chargera lorsque l'interface utilisateur de l'écran squelette s'affichera.
Pour en savoir plus, consultez la documentation.
Squelette de chargement réactif
Avantages
- Il est basé sur une API et comporte un composant avec des accessoires pour toute personnalisation.
- Il peut être utilisé comme composant de squelette séparé et directement à l'intérieur de n'importe quel composant, il est donc flexible.
- Il prend en charge la thématisation et l'animation Pulse.
Les inconvénients
- Il est facile à mettre en œuvre pour une interface utilisateur squelette simple, mais compliqué pour les squelettes plus complexes.
- Le fait d'avoir un composant squelette séparé rendra plus difficile la maintenance lorsque l'interface utilisateur et les styles changent.
Voici un exemple de React Loading Skeleton :
import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; const SkeletonComponent = () => ( <SkeletonTheme color="#202020" highlightColor="#444"> <section> <Skeleton height={50} width={50} /> </section> </SkeletonTheme> );
Nous avons importé Skeleton
et SkeletonTheme
depuis la bibliothèque react-loading-skeleton
, puis créé un composant fonctionnel qui restitue le composant SkeletonTheme
, avec color
et hightlightColor
comme propriétés.
Le composant SkeletonTheme
est utilisé pour la thématisation (par exemple, l'ajout d'effets de couleur à l'interface utilisateur squelette).
Enfin, à l'intérieur de la section, nous définissons le composant Skeleton
, avec les propriétés de hauteur et de largeur et leurs valeurs appropriées transmises.
Construire une interface utilisateur d'écran squelette de type YouTube
Créons un écran squelette de type YouTube, en utilisant React Loading Skeleton, pour montrer comment fonctionne une interface utilisateur squelette.
Configurer React
Le moyen le plus simple de configurer React consiste à utiliser Create React App, qui est "un moyen officiellement pris en charge pour créer des applications React d'une seule page. Il offre une configuration de construction moderne sans configuration.
Nous l'utiliserons pour amorcer l'application que nous allons construire. Depuis votre terminal, exécutez la commande ci-dessous :
npx create-react-app skeleton-screens && cd skeleton-screens
Une fois l'installation terminée, démarrez le serveur React en exécutant npm start
:
Créer l'interface utilisateur YouTube sans écran squelette
Tout d'abord, entrons des données factices YouTube. Des points de terminaison réels seraient normalement utilisés à la place de données factices, mais dans ce didacticiel, nous utiliserons des données factices.
Créez un fichier dans votre dossier src/
et nommez-le data.js
, ajoutez-y le code suivant.
const dummyData= [ { section: "Recommended", channel: "CNN", items: [ { id: "fDObf2AeAP4", image: "https://img.youtube.com/vi/fDObf2AeAP4/maxresdefault.jpg", title: "75 million Americans ordered to stay home", views: "1.9M views", published: "3 days agos" }, { id: "3AzIgAa0Cm8", image: "https://img.youtube.com/vi/3AzIgAa0Cm8/maxresdefault.jpg", title: "Gupta: The truth about using chloroquine to fight coronavirus pandemic", views: "128K views", published: "4 hours ago" }, { id: "92B37aXykYw", image: "https://img.youtube.com/vi/92B37aXykYw/maxresdefault.jpg", title: "Willie Jones STUNS Simon Cowell In Pitch Perfect Performance of 'Your Man'!", views: "2.47 million views", published: "1 month ago" }, { id: "J6rVaFzOEP8", image: "https://img.youtube.com/vi/J6rVaFzOEP8/maxresdefault.jpg", title: "Guide To Becoming A Self-Taught Software Developer", views: "104K views", published: "17 days ago" }, { id: "Wbk8ZrfU3EM", image: "https://img.youtube.com/vi/Wbk8ZrfU3EM/maxresdefault.jpg", title: "Tom Hanks and Rita Wilson test positive for coronavirus", views: "600k views", published: "1 week ago" }, { id: "ikHpFgKJax8", image: "https://img.youtube.com/vi/ikHpFgKJax8/maxresdefault.jpg", title: "Faces Of Africa- The Jerry Rawlings story", views: "2.3 million views", published: "2014" } ] }, { section: "Breaking News", channel: "CGTN America", items: [ { id: "tRLDPy1A8pI", image: "https://img.youtube.com/vi/tRLDPy1A8pI/maxresdefault.jpg", title: "Is Trump blaming China for COVID-19? You decide.", views: "876k views", published: "9 days ago" }, { id: "2ulH1R9hlG8", image: "https://img.youtube.com/vi/2ulH1R9hlG8/maxresdefault.jpg", title: "Journalist still goes to office during pandemic, see her daily routine", views: "873 views", published: "3 hours ago" }, { id: "TkfQ9MaIgU", image: "https://img.youtube.com/vi/_TkfQ9MaIgU/maxresdefault.jpg", title: "How are small businesses going to survive the economic downturn of the COVID-19 era?", views: "283 views", published: "4 day ago" } ] } ]; export default dummyData;
Pour reproduire le format de YouTube, nous avons créé des données factices contenant un tableau d'objets, avec des propriétés telles que l'ID, l'image, le titre, le nombre de vues et la date de publication.
Ensuite, créons notre interface utilisateur YouTube. Nous aurons trois composants :
Card | Contient les détails de la vignette, du titre, du nombre de vues, de la date de publication et de la chaîne de la vidéo. |
CardList | Renvoie toutes les cartes d'affilée. |
App | Monte notre objet dummyData , charge le squelette de l'interface utilisateur pendant deux secondes et renvoie le composant CardList . |
Dans votre dossier src
, créez un dossier et nommez-le components
. Dans le dossier des components
, créez un fichier Card.js
, ajoutez-y le code suivant :
import React from "react"; const Card = ({ item, channel }) => { return ( <li className="card"> <a href={`https://www.youtube.com/watch?v=${item.id}`} target="_blank" rel="noopener noreferrer" className="card-link" > <img src={item.image} alt={item.title} className="card-image" /> <img src={item.image} alt={item.title} className="channel-image" /> <h4 className="card-title">{item.title}</h4> <p className="card-channel"> <i>{channel}</i> </p> <div className="card-metrics"> {item.views} • {item.published} </div> </a> </li> ); }; export default Card;
Nous avons créé un composant Card
. À l'intérieur, nous avons importé React
à partir de react
, et nous avons déconstruit les accessoires item
et channel
afin qu'ils puissent être utilisés dans le composant Card
. Chaque composant d'élément de Card
qui affiche une vidéo affichera la vignette, le nombre de vues, la date de publication et le titre.
Composant CardListCardList Component
Dans le dossier des components
, créez un fichier CardList.js et ajoutez-y le code suivant :
import React from "react"; import Card from "./Card"; const CardList = ({ list }) => { return ( <ul className="list"> {list.items.map((item, index) => { return <Card key={index} item={item} channel={list.channel} />; })} </ul> ); }; export default CardList;
Dans ce composant, nous avons importé le composant Card
que nous avons créé. La carte accepte les accessoires item
et channel
, que nous obtenons en mappant via le list.items
. Nous exportons ensuite ce composant en tant que CardList
, car nous l'utiliserons dans notre composant App
.
Remarque : Le tableau d'éléments qui est mappé dans ce composant est le tableau d'objets dans notre dummyData
.
Composant d'application
Dans le fichier app.js du répertoire src/
, supprimez le code qui s'y trouve et ajoutez-y ce qui suit.
import React, { useState, useEffect } from "react"; import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { setLoading(true); const timer = setTimeout(() => { setVideos(dummyData); setLoading(false); }, 5000); return () => clearTimeout(timer); }, []); return ( <div className="App"> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> ); }; export default App;
Dans ce composant, nous avons importé les useState
et useEffect
avec React
et les autres fichiers que nous avons créés et qui seront nécessaires dans le composant App
.
Étant donné que nos données sont des données factices, nous devons les modéliser comme les données de l'API en chargeant le contenu après un délai de deux secondes, à l'aide de la méthode JavaScript setTimeout
.
Ensuite, dans le composant App
, nous créons un état vidéo et définissons l'état sur un tableau vide à l'aide de useState
.
Pour charger nos données factices, nous utiliserons le crochet useEffect
. Dans notre hook, nous créons une variable timer qui contient la fonction setTimeout
()
. Dans la fonction, nous définissons notre état vidéo sur notre objet dummyData
, et nous nous assurons que les données se chargent après deux secondes, et, enfin, nous annulons la minuterie lors du démontage.
Enfin, nous mappons notre état vidéo et renvoyons l'élément de section qui contient la list-section
et le composant CardList
avec ses accessoires de liste.
Ajout de CSS
Jusqu'à présent, nous avons utilisé beaucoup de classes sans véritable CSS. Dans le dossier src
, supprimez tout dans App.css
et remplacez-le par le code suivant ;
.App { max-width: 960px; margin: 0 auto; font-size: 16px; } .list { display: flex; justify-content: space-between; flex-wrap: wrap; list-style: none; padding: 0; } .section-title { margin-top: 30px; } .card { width: calc(33% - 10px); margin: 20px 0; } .card-link { color: inherit; text-decoration: none; } .card-image { width: 100%; } .channel-image { border-radius: 100%; padding: 0, 10px, 0, 0; width: 40px; height: 40px; } .card-title { margin-top: 10px; margin-bottom: 0; } .card-channel { margin-top: 5px; margin-bottom: 5px; font-size: 14px; } /* Tablets */ @media (max-width: 1000px) { .App { max-width: 600px; } .card { width: calc(50% - 22px); } } /* Mobiles \*/ @media (max-width: 640px) { .App { max-width: 100%; padding: 0 15px; } .card { width: 100%; } }
Voyons à quoi ressemble notre interface utilisateur YouTube sans l'écran squelette. Vous pouvez voir que lorsque la page se charge, un écran blanc apparaît pendant deux secondes, puis les données se chargent rapidement.
Utilisation du squelette de chargement React
Contrairement à d'autres bibliothèques dans lesquelles vous créeriez méticuleusement un écran squelette pour correspondre aux tailles de police, aux hauteurs de ligne et aux marges de votre contenu, le composant Skeleton
est conçu pour être utilisé directement dans vos composants, à la place du contenu qui se charge.
Passons en revue quelques raisons pour lesquelles nous avons choisi React Loading Skeleton plutôt que d'autres.
Thématisation
React Loading Skeleton prend en charge la thématisation. Ainsi, vous pouvez facilement changer les couleurs de tous les composants du squelette en utilisant SkeletonTheme
et transmettre des valeurs aux props
de couleur.
Ci-dessous un exemple montrant comment cela fonctionne :
import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; <SkeletonTheme color="grey" highlightColor="#444"> <p> <Skeleton height={250} width={300} count={1} /> </p> </SkeletonTheme> <SkeletonTheme color="#990" highlightColor="#550"> <p> <Skeleton height={250} width={300} count={1} /> </p> </SkeletonTheme>
Durée
En plus des accessoires height
, width
et color
, nous pouvons également spécifier un accessoire de duration
.
<Skeleton duration={2} />
La durée par défaut est 1.2
. Cela détermine combien de temps il faut pour faire un cycle de l'animation squelette.
Pour en savoir plus, consultez la documentation.
Implémentation de l'interface utilisateur de l'écran squelette
Maintenant, nous allons installer react-loading-skeleton
. Exécutez la commande suivante dans votre terminal pour installer le package :
npm install react-loading-skeleton
Composant squelette
Créons un composant squelette pour nos données vidéo. Dans notre dossier de components
, créez un fichier SkeletonCard.js
et ajoutez le code suivant :
import React from "react"; import Skeleton from "react-loading-skeleton"; const SkeletonCard = () => { return ( <section> <h2 className="section-title"> <Skeleton height={30} width={300} /> </h2> <ul className="list"> {Array(9) .fill() .map((item, index) => ( <li className="card" key={index}> <Skeleton height={180} /> <h4 className="card-title"> <Skeleton circle={true} height={50} width={50} /> <Skeleton height={36} width={`80%`} /> </h4> <p className="card-channel"> <Skeleton width={`60%`} /> </p> <div className="card-metrics"> <Skeleton width={`90%`} /> </div> </li> ))} </ul> </section> ); }; export default SkeletonCard;
Nous avons créé une liste non ordonnée. À l'intérieur, nous avons utilisé la méthode Array.fill()
. Comme nous avons neuf éléments de données factices, nous avons utilisé la méthode Array.fill()
pour parcourir la longueur de notre objet items
et l'avons rempli sans valeur d'index, rendant ainsi notre tableau vide . Voir la documentation Array.fill pour savoir comment cela fonctionne.
Ensuite, nous avons mappé notre tableau vide pour renvoyer une liste contenant les propriétés du squelette, et nous avons spécifié la valeur de chacune des propriétés du squelette.
Ici, la height
évoque la longueur d'un rectangle squelette et la width
fait référence à la largeur, tandis que le circle
crée la partie arrondie de l'interface utilisateur squelette.
React Loading Skeleton est livré avec une animation Pulse par défaut, ce qui le rend pratique. Vous pouvez créer une animation Pulse adaptée à votre projet, mais si vous me le demandez, je m'en tiendrai à la valeur par défaut.
Enfin, le code source complet est disponible.
Nous avons maintenant une interface utilisateur d'écran squelette entièrement fonctionnelle. Notre exemple montre le squelette pendant cinq secondes avant d'afficher le contenu.
Voyons notre résultat jusqu'à présent :
Conclusion
Les écrans squelettes améliorent considérablement l'expérience utilisateur en évitant la frustration de faire face à un écran entièrement vide et en donnant à l'utilisateur une idée de ce à quoi ressemblera le contenu avant qu'il ne se charge.
Si vous n'êtes pas à l'aise avec l'un des packages que nous avons examinés, vous pouvez créer votre propre squelette d'interface utilisateur en créant des rectangles et des cercles qui imitent la mise en page de la page.
Veuillez partager vos commentaires et votre expérience dans la section des commentaires ci-dessous. J'aimerais voir ce que vous proposez!
Le référentiel de support pour cet article est disponible sur Github.
Les références
- "Tout ce que vous devez savoir sur les écrans squelettes", Bill Chung, UX Collective
- "Squelette de chargement des pages avec React", Anthony Panagi, Octopus Wealth
- "Écrans squelettes avec React et React Native", Chris Dolphin, Alligator.io
- "Mise en œuvre du chargement de squelette dans React", Adrian Bece, DEV