Aller au contenu

La librairie SWR

La libraire SWR est développée par le même éditeur que le framework Next.js.

Contexte

Traditionnellement, un composant React qui affiche des données commence par les charger dans son état, afin de pouvoir l'afficher. Les données externes, récupérées par HTTP(S), constituent un état distant, par opposition à l'état local (useState). Cet état peut évoluer indépendamment de l'application.

Il existe donc des librairies dédiées à la gestion de cet état distant, notamment SWR et react-query. Elles appliquent le mécanisme "stale while revalidate" :

  1. La librairie charge les données par HTTP, et les met en cache
  2. Lors de l'appel suivant à la même URL, elle renvoie les données "stale" depuis le cache…
  3. … et, en parallèle, renvoie une requête HTTP et met à jour les données ("revalidate")

Du point de vue de l'utilisateur/trice, le résultat est le suivant :

  • À la première requête, il/elle voit un délai de réponse (envoi de la requête, traitement sur le backend, envoi de la réponse) avant l'affichage des résultats.
  • Aux requêtes suivantes, le réaffichage est immédiat, mais certaines données peuvent se remettre à jour après une fraction de seconde.

Fonctionnalités de SWR

A cette fonctionnalité de base, la librairie SWR en rajoute un certain nombre d'autres : * rafraîchissement automatique des ressources dans certaines circonstance (par exemple, sur un navigateur, lorsque la page revient au premier plan) * rafraîchissement périodique en option * rafraîchissement déclenché par l'utilisateur (ex. : bouton "Reload") * rafraîchissement conditionnel * pagination (quand le nombre d'items de données est potentiellement important) * …

SWR propose essentiellement : * un hook, useSWR, permettant d'envoyer les requêtes * un composant SWRConfig permettant de spécifier les options par défaut

Exemple d'utilisation

Voici un exemple d'utilisation très basique. Il suppose que SWR a été configuré par un SWRConfig (voir ci-dessous le paragraphe sur la fonction "fetcher").

import useSWR from 'swr'

function AllUsers() {

  const { data, error, isLoading, mutate } = useSWR('/api/users')

  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>

  return (
    {/* Affiche la liste des utilisateurs. Cette liste utilise 
        - `data` pour accéder aux données, et
        - `mutate` pour déclencher un raffraichissement
    */}
  )
}

Le hook useSWR renvoie un objet composé de plusieurs membres : * data contient les données éventuellement reçues, * error indique si une erreur est survenue pendant l'envoi de la requête, * isLoading indique si le chargement est en cours, * mutate est une fonction qui permet de demander la revalidation des données – par exemple, si on a supprimé une entrée dans la liste - bouton "Supprimer", * …

Bien entendu, on peut récupérer et utiliser seulement ceux dont on a besoin.

Avantages : * pas besoin de useState pour stocker les données reçues, * pas besoin de useEffect pour charger les données au montage du composant, * syntaxe généralement plus claire et plus concise * meilleure réactivité apparente pour l'utilisateur

Attention : étant donné que SWR peut réexécuter la requête de façon plus ou moins imprévisible, il est hors de question de l'utiliser dans des cas où des actions doivent être effectuées, comme par exemple, ajouter des entrées dans une table. Dans ce cas, utiliser obligatoirement fetch ou axios.

Le "fetcher"

SWR a besoin d'une fonction utilitaire pour exécuter les requêtes et récupérer les données. Le cas le plus simple est :

const fetcher = url => fetch(url).then(r => r.json());
const { data, error } = useSWR('/api/users', fetcher);

Le "fetcher" peut être personnalisé, comme on le verra dans la section suivante.

Configuration globale

Comme pour d'autres fonctionnalités (navigation, contextes, systèmes d'authentification, …), la configuration SWRConfig doit "emballer" ("wrap") l'application principale :

export default function KivAppA () {

  // Variables d'état, etc.

  // Ce "fetcher" utilise Axios, et ajoute l'URL de base et le token d'authentification
  const fetcher = async (url) => {
    const res = await axios.get(baseUrl + url,
      { headers: { Authorization: `Bearer ${authToken}` } });
    return res.data;
  }

  return (
    <SWRConfig value={{ fetcher }}>
      <AppContext.Provider value={{/* ... */}}>
        <Root/>
      </AppContext.Provider>
    </SWRConfig>
  );
}

Astuces avancées

  • Il est possible de configurer le fetcher pour qu'il inclue le token d'authentification. Ce type de configuration est utile dans les cas où l'identité de l'utilisateur peut changer, et qu'il faut impérativement récupérer les données correctes pour le nouvel utilisateur. Voir ici.
const data = useSWR([`/api/items/${id}`, authToken])
  • Requête conditionnelle : en raison du caractère asynchrone du code React, il peut arriver qu'une requête SWR paramétrée soit exécutée alors que son paramètre n'a pas encore reçu sa valeur, par exemple
const data = useSWR(`/api/items/${id}`)

Par conséquent, une requête HTTP va être émise pour rien (et échouer). Pour éviter ce cas, il est possible de rendre la requête conditionnelle :

const data = useSWR(id ? `/api/items/${id}` : null)