Fermer

juin 22, 2020

React Hooks pour l'extraction de données à distance


À propos de l'auteur

Ibrahima Ndaw est un développeur et blogueur à pile complète qui aime JavaScript et se familiarise également avec la conception d'interface utilisateur / UX.
En savoir plus sur
Ibrahima

Dans cet article, nous étudierons une nouvelle façon de récupérer des données dans React Apps, appelée SWR. Il s'agit d'un ensemble de crochets pour l'extraction de données à distance qui facilite les choses, telles que la mise en cache, la pagination, etc. Nous allons également créer une application Pokedex à partir de zéro et utiliser les fonctionnalités SWR pour obtenir des données et les paginer.

SWR est une bibliothèque légère créée par Vercel (anciennement ZEIT) qui permet de récupérer, de mettre en cache ou de récupérer des données en temps réel à l'aide de React Crochets. Il est construit avec React Suspense qui permet à vos composants «d'attendre» quelque chose avant de pouvoir effectuer le rendu, y compris les données. SWR est également livré avec d'excellentes fonctionnalités telles que la récupération dépendante, la concentration sur la revalidation, la récupération de la position de défilement, etc. C'est également un outil très puissant car il est indépendant du backend et prend bien en charge TypeScript. C'est un paquet qui a un bel avenir.

Pourquoi devriez-vous vous en soucier? Faites attention si vous recherchez une bibliothèque qui non seulement récupère les données des API, mais permet également de faire des choses comme la mise en cache et la récupération dépendante. Ce qui sera couvert dans ce tutoriel sera utile lors de la création d'applications React avec beaucoup de pièces mobiles. On s'attend à ce que vous ayez dû utiliser Axios et l'API Fetch, même si nous comparerons en quoi ils diffèrent de SWR, nous n'entrerons pas dans les détails sur la façon dont ils seront mis en œuvre.

Dans ce guide, Je vais vous présenter React Hooks for Remote Data Fetching en créant une application Pokedex qui demande des données à l'API Pokemon. Nous allons également plonger dans d'autres fonctionnalités fournies avec SWR, et souligner ses différences par rapport aux solutions populaires telles que l'API Fetch et la bibliothèque Axios et vous donner les raisons pour lesquelles utiliser cette bibliothèque et pourquoi vous devriez garder un œil sur SWR.

Commençons donc par répondre à une question fondamentale: qu'est-ce que le SWR?

Qu'est-ce que le SWR?

SWR est un initialisme de rassis pendant la revalidation. Il s'agit d'une bibliothèque React Hooks pour l'extraction de données à distance. SWR fonctionne en trois étapes principales: tout d'abord, il renvoie les données du cache (la partie périmée), puis envoie la demande de récupération (la partie revalidée), et enfin, les données à jour. Mais pas de soucis, SWR gère toutes ces étapes pour nous. La seule chose que nous devons faire est de donner au crochet useSWR les paramètres nécessaires pour faire la demande.

SWR a aussi quelques fonctionnalités intéressantes telles que:

  • Agnostique back-end
  • Page rapide navigation
  • Revalidation sur focus
  • Interrogation d'intervalle
  • Demande de déduplication
  • Mutation locale
  • Pagination
  • TypeScript ready
  • Prise en charge SSR
  • Mode suspense
  • React Native support

Ça a l'air magique? Eh bien, SWR simplifie les choses et augmente à coup sûr l'expérience utilisateur de votre application React. Et une fois que nous commencerons à l'implémenter dans notre projet, vous comprendrez pourquoi ce crochet est pratique.

Il est important de savoir que le nom du package est swr ou SWR et le crochet utilisé pour obtenir les fonctionnalités SWR est nommé useSWR .

En théorie, le SWR est peut-être ce dont vous avez besoin pour améliorer la récupération de vos données. Cependant, nous avons déjà deux excellentes façons de faire des requêtes HTTP dans notre application: l'API Fetch et la bibliothèque Axios.

Alors, pourquoi utiliser une nouvelle bibliothèque pour récupérer des données? essayons de répondre à cette question légitime dans la section suivante.

Comparaison avec Fetch et Axios

Nous avons déjà de nombreuses façons de faire des requêtes HTTP dans nos applications React, et deux des plus populaires sont l'API Fetch et la bibliothèque Axios . Ils sont tous deux excellents et nous permettent de récupérer ou d'envoyer des données facilement. Cependant, une fois l'opération terminée, ils ne nous aideront pas à mettre en cache ou à paginer les données, vous devez le faire nous-mêmes.

Axios ou Fetch traitera simplement la demande et renverra la réponse attendue, rien de plus. [19659005] Et par rapport au SWR, c'est un peu différent parce que le SWR sous le capot utilise l'API Fetch pour demander des données au serveur – c'est une sorte de couche construite par-dessus. Cependant, il possède de belles fonctionnalités telles que la mise en cache, la pagination, la récupération de la position de défilement, la récupération dépendante, etc., et pour être précis un certain niveau de réactivité hors de la boîte qu'Axios ou Fetch ne possède pas. C'est un gros avantage car avoir de telles fonctionnalités contribue à rendre nos applications React rapides et conviviales et à réduire considérablement la taille de notre code.

Et pour conclure, gardez à l'esprit que SWR n'est pas identique à Axios ou Fetch même si cela aide à traiter les requêtes HTTP. SWR est plus avancé qu'eux, il fournit des améliorations pour garder notre application synchronisée avec le back-end et augmente ainsi les performances de notre application.

Nous savons maintenant quelles sont les différences que SWR a par rapport à la bibliothèque Axios ou à l'API Fetch, il est temps de découvrir pourquoi utiliser un tel outil.

Lecture recommandée : Consommation des API REST pour réagir avec Fetch et Axios

Pourquoi utiliser SWR pour l'extraction de données?

Comme je a déclaré plus tôt SWR est livré avec quelques fonctionnalités pratiques qui aident à augmenter la convivialité de votre application facilement. Avec SWR, vous pouvez paginer vos données en un rien de temps en utilisant useSWRPages vous pouvez également récupérer des données qui dépendent d'une autre demande ou récupérer une position de défilement lorsque vous revenez à une page donnée, et bien plus encore.

Habituellement, nous montrons à l'utilisateur un message de chargement ou un spinner lors de la récupération des données du serveur. Et avec SWR, vous pouvez l'améliorer en montrant à l'utilisateur les données mises en cache ou périmées tout en récupérant de nouvelles données à partir de l'API. Et une fois cette opération terminée, elle revalidera les données pour afficher la nouvelle version. Et vous n'avez rien à faire, SWR mettra en cache les données la première fois que vous les récupérerez et les récupérera automatiquement lorsqu'une nouvelle demande est faite.

Jusqu'à présent, nous voyons déjà pourquoi utiliser SWR sur Axios ou Fetch est préférable en fonction évidemment de ce que vous visez à construire. Mais dans de nombreux cas, je recommanderai d'utiliser SWR car il possède de grandes fonctionnalités qui vont au-delà de la simple récupération et du renvoi de données.

Cela dit, nous pouvons maintenant commencer à créer notre application React et utiliser la bibliothèque SWR pour récupérer des données distantes. [19659005] Donc, commençons par configurer un nouveau projet.

Configuration

Comme je l'ai dit plus tôt dans l'introduction, nous allons créer une application qui récupère les données de l'API Pokemon. Vous pouvez également utiliser une API différente si vous le souhaitez, je m'en tiendrai à ce moment.

Et pour créer une nouvelle application, nous devons exécuter la commande suivante sur le terminal:

 npx create-react-app react -swr 

Ensuite, nous devons installer la bibliothèque SWR en naviguant d'abord vers le dossier contenant l'application React.

 cd react-swr 

Et exécutez sur le terminal la commande suivante pour installer le package SWR.

 yarn add swr 

Ou si vous utilisez npm:

 npm install swr 

Maintenant que nous avons tous configuré, structurons le projet comme suit pour commencer à utiliser SWR:

 src
├── composants
| └── Pokemon.js
├── App.js
├── App.test.js
├── index.js
├── serviceWorker.js
├── setupTests.js
├── package.json
├── README.md
├── yarn-error.log
└── yarn.lock 

Comme vous pouvez le voir, la structure des dossiers est simple. La seule chose à noter est le dossier des composants qui contient le fichier Pokemon.js . Il sera utilisé plus tard comme composant de présentation pour montrer un seul Pokémon une fois que nous aurons obtenu les données de l'API.

Génial! Avec cela en place, nous pouvons maintenant commencer à récupérer des données à partir de l'API en utilisant useSWR .

Récupération de données à distance

Le package SWR a quelques fonctionnalités pratiques comme nous l'avons vu ci-dessus. Cependant, il existe deux façons de configurer cette bibliothèque: localement ou globalement.

Une configuration locale signifie que chaque fois que nous créons un nouveau fichier, nous devons reconfigurer SWR pour pouvoir récupérer des données distantes. Et une configuration globale nous permet de réutiliser une partie de notre configuration dans différents fichiers car une fonction de récupération peut être déclarée une fois et utilisée partout.

Et pas de soucis, nous verrons les deux dans cet article, mais pour l'instant, mettons la main à la pâte et ajoutons du code significatif dans le fichier App.js .

Affichage des données

 importez React de 'react'
importer useSWR depuis 'swr'
importer {Pokemon} depuis './components/Pokemon'

const url = 'https://pokeapi.co/api/v2/pokemon'

const fetcher = (... args) => fetch (... args) .then ((res) => res.json ())

fonction App () {
    const {data: result, error} = useSWR (url, fetcher)

    si (erreur) retourne 

Quelque chose a mal tourné!

if (! result) return

Chargement ...

revenir (

Pokedex

{result.results.map ((pokemon) => ( ))}
) } export app par défaut

Comme vous pouvez le voir, nous commençons par importer useSWR à partir de la bibliothèque SWR. Cela déclare l'URL de l'API à partir de laquelle vous souhaitez obtenir des données et une fonction pour récupérer ces données.

La fonction récupérateur est utilisée ici pour transformer les données en JSON. Il reçoit les données extraites comme argument et renvoie quelque chose.

Notez qu'ici, j'utilise l'opérateur Rest ( (... args) ) car je ne suis pas sûr du type et de la longueur des données reçues en tant que paramètre, je copie donc tout avant de le passer à nouveau comme argument à la méthode fetch fournie par useSWR qui transforme les données en JSON et

Cela dit, le récupérateur et l'url de l'API peuvent maintenant être transmis en tant que paramètres au crochet useSWR . Avec cela, il peut maintenant faire la demande et il retourne deux états: les données récupérées et un état d'erreur. Et data: result est le même que data.result nous utilisons la déstructuration des objets pour extraire result des données .

Avec les valeurs retournées, nous pouvons maintenant vérifier si les données sont récupérées avec succès, puis les parcourir. Et pour chaque utilisateur, utilisez le composant Pokemon pour l'afficher.

Maintenant que nous avons les données et les transmettre au composant Pokemon, il est temps de mettre à jour Pokemon.js pour pouvoir recevoir et afficher

Création du composant Pokemon

 importez React de 'react'
importer useSWR depuis 'swr'

const fetcher = (... args) => fetch (... args) .then ((res) => res.json ())

exporter const Pokemon = ({pokemon}) => {
    const {name} = pokemon
    const url = 'https://pokeapi.co/api/v2/pokemon/' + nom

    const {data, error} = useSWR (url, récupérateur)

    si (erreur) retourne 

Quelque chose a mal tourné!

if (! data) return

Chargement ...

revenir (
# {data.id} {name}

{name}

{data.types.map ((poke) => poke.type.name) .join (',')}
) }

Ici, nous avons un composant qui reçoit une seule donnée Pokemon de l'API et l'affiche. Cependant, les données reçues ne contiennent pas tous les champs nécessaires, nous devons donc faire une autre demande à l'API pour obtenir l'objet Pokemon complet.

Et comme vous pouvez le voir, nous utilisons le même processus pour récupérer les données même si cela au moment où nous ajoutons le nom du Pokémon à l'URL.

Soit dit en passant, si vous n'êtes pas familier avec la déstructuration, ({pokemon}) revient à recevoir des accessoires et à accéder à l'objet pokemon avec props.pokemon . C'est juste un raccourci pour extraire des valeurs d'objets ou de tableaux.

Avec cela en place, si vous accédez au dossier racine du projet et exécutez sur le terminal la commande suivante:

 yarn start 

Ou si vous utilisez npm:

 npm start 

Vous devriez voir que les données sont correctement récupérées à partir de l'API Pokemon et affichées comme prévu.

 fetching
Fetching illustration. ( Grand aperçu )

Génial! Nous pouvons désormais récupérer des données distantes avec SWR. Cependant, cette configuration est locale et peut être un peu redondante car vous pouvez déjà voir que App.js et Pokemon.js utilisent la même fonction de récupération pour faire la même chose.

Mais heureusement, le package est livré avec un fournisseur pratique nommé SWRConfig qui aide à configurer SWR globalement. C'est un composant wrapper qui permet aux composants enfants d'utiliser la configuration globale et donc la fonction de récupération.

Pour configurer SWR globalement, nous devons mettre à jour le fichier index.js car c'est là que le composant App est rendu en utilisant React DOM. Si vous le souhaitez, vous pouvez utiliser SWRConfig directement dans le fichier App.js .

Configuration de SWR globalement

 import React from 'react'
importer ReactDOM depuis 'react-dom'
importer {SWRConfig} depuis 'swr'
importer l'application depuis './App'
import './index.css'

const fetcher = (... args) => fetch (... args) .then ((res) => res.json ())

ReactDOM.render (
    
        
            
        
    ,
    document.getElementById ('root')

Comme vous pouvez le voir, nous commençons par importer SWRConfig qui est un fournisseur qui doit encapsuler le composant supérieur ou juste une partie de votre application React qui doit utiliser les fonctionnalités SWR. Il prend comme accessoire une valeur qui attend un objet de configuration. Vous pouvez passer plusieurs propriétés à l'objet config, ici j'ai juste besoin de la fonction pour récupérer des données.

Maintenant, au lieu de déclarer la fonction fetcher dans chaque fichier, nous la créons ici et la transmettons comme valeur à SWRConfig . Avec cela, nous pouvons maintenant récupérer des données à n'importe quel niveau dans notre application sans créer une autre fonction et donc éviter la redondance.

En plus de cela, fetcher est égal à fetcher: fetcher c'est juste du sucre syntaxique proposé par ES6. Avec ce changement, nous devons maintenant mettre à jour nos composants pour utiliser la configuration globale.

Utilisation de la configuration SWR globale

 importez React depuis 'react'
importer useSWR depuis 'swr'
importer {Pokemon} depuis './components/Pokemon'

const url = 'https://pokeapi.co/api/v2/pokemon'

fonction App () {
    const {data: result, error} = useSWR (url)

    si (erreur) retourne 

Quelque chose a mal tourné!

if (! result) return

Chargement ...

revenir (

Pokedex

{result.results.map ((pokemon) => ( ))}
) } export default App

Il ne nous reste plus qu'à passer la méthode url à useSWR au lieu de passer la url et fetcher . Modifions également un peu le composant Pokemon.

 importez React depuis "react"
importer useSWR depuis 'swr'

exporter const Pokemon = ({pokemon}) => {
    const {name} = pokemon
    const url = 'https://pokeapi.co/api/v2/pokemon/' + nom

    const {data, error} = useSWR (url)

    si (erreur) retourne 

Quelque chose a mal tourné!

if (! data) return

Chargement ...

revenir (
# {data.id} {name}

{name}

{data.types.map ((poke) => poke.type.name) .join (',')}
) }

Vous pouvez déjà voir que nous n'avons plus de fonction de récupération, grâce à la configuration globale qui transmet la fonction à useSWR sous le capot.

Maintenant, vous pouvez utiliser la fonction de récupération globale partout dans votre application. La seule chose dont le hook useSWR a besoin pour récupérer des données distantes est l'URL.

Cependant, nous pouvons encore améliorer la configuration en créant un hook personnalisé pour éviter de déclarer l'URL encore et encore, et à la place , passez simplement en paramètre le chemin d'accès.

Configuration avancée en créant un crochet personnalisé

Pour ce faire, vous devez créer un nouveau fichier à la racine du projet nommé useRequest.js (vous pouvez le nommer comme vous voulez) et ajoutez-y ce bloc de code ci-dessous.

 import useSwr from 'swr'

const baseUrl = 'https://pokeapi.co/api/v2'

export const useRequest = (chemin, nom) => {
    si (! chemin) {
        jeter une nouvelle erreur ('Le chemin est requis')
    }

    const url = nom? baseUrl + chemin + '/' + nom: baseUrl + chemin
    const {data, error} = useSwr (url)

    retourner {données, erreur}
} 

Ici, nous avons une fonction qui reçoit un chemin et éventuellement un nom et l'ajoute à l'URL de base pour créer l'URL complète. Ensuite, il vérifie si un paramètre de nom est reçu ou non et le gère en conséquence.

Ensuite, cette URL est transmise en tant que paramètre au hook useSWR pour pouvoir récupérer les données distantes et les renvoyer. . Et si aucun chemin n'est transmis, cela génère une erreur.

Génial! nous devons maintenant modifier un peu les composants pour utiliser notre crochet personnalisé.

 importez React à partir de 'react'
importer {useRequest} depuis './useRequest'
import './styles.css'
importer {Pokemon} depuis './components/Pokemon'

fonction App () {
    const {data: result, error} = useRequest ('/ pokemon')

    si (erreur) retourne 

Quelque chose a mal tourné!

if (! result) return

Chargement ...

revenir (

Pokedex

{result.results.map ((pokemon) => ( ))}
) } export default App

Maintenant, au lieu d'utiliser le hook SWR, nous utilisons le hook personnalisé construit au-dessus de lui puis passons comme prévu le chemin comme argument. Avec cela en place, tout fonctionnera comme avant mais avec une configuration beaucoup plus propre et flexible.

Mettons également à jour le composant Pokemon.

 importez React de 'react'
importer {useRequest} à partir de '../useRequest'

exporter const Pokemon = ({pokemon}) => {
    const {name} = pokemon
    const {data, error} = useRequest ('/ pokemon', nom)

    si (erreur) retourne 

Quelque chose a mal tourné!

if (! data) return

Chargement ...

revenir (
# {data.id} {name}

{name}

{data.types.map ((poke) => poke.type.name) .join (',')}
) }

Vous pouvez déjà voir comment notre crochet personnalisé rend les choses plus faciles et plus flexibles. Ici, nous avons juste besoin de passer en plus le nom du Pokémon à récupérer pour useRequest et il gère tout pour nous.

J'espère que vous commencerez à profiter de cette bibliothèque cool parce que SWR offre tellement de fonctionnalités, et l'une d'elles est useSWRPages qui est un crochet pour paginer facilement les données. Donc, utilisons ce crochet dans le projet.

Paginez nos données avec useSWRPages

SWR nous permet de paginer facilement les données et de n'en demander qu'une partie, et si nécessaire de récupérer les données à afficher pour la page suivante.

Maintenant, créons un nouveau fichier à la racine du projet usePagination.js et utilisons-le comme hook personnalisé pour la pagination.

 import React from 'react'
importer useSWR, {useSWRPages} depuis 'swr'
importer {Pokemon} depuis './components/Pokemon'

export const usePagination = (chemin) => {
    const {pages, isLoadingMore, loadMore, isReachingEnd} = useSWRPages (
        'pokemon-page',
        ({offset, withSWR}) => {
            const url = offset || `https://pokeapi.co/api/v2$ {path}`
            const {data: result, error} = withSWR (useSWR (url))

            si (erreur) retourne 

Quelque chose a mal tourné!

if (! result) return

Chargement ...

return result.results.map ((pokemon) => ( )) }, (SWR) => SWR.data.next, [] ) retourner {pages, isLoadingMore, loadMore, isReachingEnd} }

Comme vous pouvez le voir, nous commençons ici par importer useSWRPages qui est l'aide qui permet de paginer facilement les données. Il reçoit 4 arguments: la clé de la requête pokemon-page qui est également utilisée pour la mise en cache, une fonction pour récupérer les données qui renvoie un composant si les données sont récupérées avec succès, et une autre fonction qui prend la SWR objet et demande des données de la page suivante, et un tableau de dépendances.

Et une fois les données récupérées, la fonction useSWRPages renvoie plusieurs valeurs, mais ici nous en avons besoin de 4 d'entre elles : les pages qui est le composant renvoyé avec les données, la fonction isLoadingMore qui vérifie si les données sont actuellement récupérées, la fonction loadMore qui permet de récupérer plus de données et la méthode isReachingEnd qui détermine s'il y a encore des données à récupérer ou non.

Maintenant que nous avons le hook personnalisé qui renvoie les valeurs nécessaires pour paginer les données, nous pouvons maintenant passer au App.js et ajustez-le un peu. [19659101] importer React depuis 'react'
importer {usePagination} depuis './usePagination'
import './styles.css'

exporter la fonction par défaut App () {
const {pages, isLoadingMore, loadMore, isReachingEnd} = usePagination (
'/ pokemon'
)

revenir (

Pokedex

{pages}



)
}

Une fois le crochet usePagination importé, nous pouvons maintenant passer le chemin en paramètre et récupérer les valeurs renvoyées. Et puisque pages est un composant, nous n'avons pas besoin de parcourir les données ou quelque chose comme ça.

Ensuite, nous utilisons la fonction loadMore sur le bouton pour extraire plus et désactivez-la si l'opération de récupération n'est pas terminée ou s'il n'y a pas de données à récupérer.

Génial! avec cette modification, nous pouvons maintenant parcourir la racine du projet et démarrer le serveur avec cette commande pour prévisualiser notre application.

 yarn start 

Ou si vous utilisez npm:

 npm start 

Vous devriez voir que les données sont récupérées avec succès et si vous cliquez sur le bouton, de nouvelles données seront récupérées par SWR.

 Pagination
Pagination. ( Grand aperçu )

Jusqu'à présent, nous avons vu dans la pratique la bibliothèque SWR, et j'espère que vous y trouverez de la valeur. Cependant, il a encore quelques fonctionnalités à offrir. Explorons ces fonctionnalités dans la section suivante.

Autres caractéristiques de SWR

La bibliothèque SWR a un tas de choses pratiques qui simplifient la façon dont nous créons des applications React.

Focus Revalidation

C'est une fonctionnalité qui permet de mettre à jour ou de revalider pour être précis les données lorsque vous recentrez une page ou basculez entre les onglets. Et par défaut, cette fonctionnalité est activée, mais vous pouvez quand même la désactiver si elle ne correspond pas à vos besoins. Cela peut être utile, surtout si vous avez des données avec des mises à jour de fréquence de haut niveau.

Refetch on Interval

La bibliothèque SWR permet de récupérer des données après un certain laps de temps. Cela peut être utile lorsque vos données changent à grande vitesse ou que vous devez faire une nouvelle demande pour obtenir un élément de nouvelles informations de votre base de données.

Mutation locale

Avec SWR, vous pouvez définir un état local temporaire qui se met à jour automatiquement lorsque de nouvelles données sont récupérées (revalidation). Cette fonctionnalité est particulièrement utile lorsque vous traitez avec une approche hors ligne, elle permet de mettre à jour facilement les données.

Récupération de la position de défilement

Cette fonctionnalité est très pratique, en particulier lorsqu'il s'agit de traiter d'énormes listes. Il vous permet de récupérer la position de défilement après être revenu à la page. Et dans tous les cas, cela augmente la convivialité de votre application.

Récupération dépendante

SWR vous permet de récupérer des données qui dépendent d'autres données. Cela signifie qu'il peut récupérer les données A, et une fois cette opération terminée, il l'utilise pour récupérer les données B tout en évitant les cascades. Et cette fonctionnalité est utile lorsque vous avez des données relationnelles.

Cela dit, SWR contribue à augmenter l'expérience utilisateur dans tous les domaines. Il a plus de fonctionnalités que cela, et dans de nombreux cas, il est préférable de l'utiliser sur l'API Fetch ou la bibliothèque Axios.

Conclusion

Tout au long de cet article, nous avons vu pourquoi SWR est une bibliothèque géniale. Il permet la récupération de données à distance à l'aide de React Hooks et aide à simplifier certaines fonctionnalités avancées telles que la pagination, la mise en cache des données, la récupération à intervalles, la récupération de la position de défilement, etc. SWR est également indépendant du backend, ce qui signifie qu'il peut extraire des données de tout type d'API ou de bases de données. En définitive, SWR augmente considérablement l'expérience utilisateur de vos applications React, il a un bel avenir et vous devriez le surveiller ou mieux l'utiliser dans votre prochaine application React.

Vous pouvez prévisualiser le projet fini en direct ici .

Merci d'avoir lu!

Prochaines étapes

Vous pouvez continuer à vérifier les liens suivants qui vous donneront une meilleure compréhension au-delà de la portée de ce didacticiel.

Lectures complémentaires sur SmashingMag:

 Éditorial Smashing (ks, ra, yk, il)




Source link

Revenir vers le haut