Skip to content
Annonce SWR 2.0

Annonce SWR 2.0

Nous sommes heureux d’annoncer la release de SWR 2.0, la populaire librairie de récupération de données pour React qui permet aux composants de récupérer, mettre en cache et muter des données et de garder l’UI à jour avec les changements de ces données au fil du temps.

Cette nouvelle version vient avec de nombreuses améliorations et de nouvelles fonctionnalités, telles que de nouvelles API de mutation, des capacités d’UI optimiste améliorées, de nouveaux outils développeur et un meilleur support du rendu simultané. Nous souhaitons remercier chaleureusement tous les contributeurs et mainteneurs qui ont rendu cette release possible.

Mutation et UI Optimiste

useSWRMutation

La mutation est une partie important du processus de récupération des données. Elles vous permettent de faire des changements dans vos données localement et à distance. Notre API mutate existante vous permet de revalider et de modifier vos données manuellement. Avec SWR 2.0, le nouveau hook useSWRMutation rend encore plus simple la modification des données à distance en utilisant une API déclarative. Vous pouvez configurer une mutation en utilisant le hook, et l’activer plus tard :

import useSWRMutation from 'swr/mutation'
 
async function sendRequest(url, { arg }) {
  return fetch(url, {
    method: 'POST',
    body: JSON.stringify(arg)
  })
}
 
function App() {
  const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
 
  return (
    <button
      disabled={isMutating}
      onClick={() => trigger({ username: 'johndoe' })}
    >{
      isMutating ? 'Creation...' : 'Utilisateur Créé'
    }</button>
  )
}

L’exemple ci-dessus définit une mutation sendRequest qui affecte la ressource ’/api/user’. Contrairement à useSWR, useSWRMutation ne démarre pas immédiatement la requête lors du rendu. Au lieu de cela, il retourne une fonction trigger qui peut être appelée plus tard pour démarrer manuellement la mutation.

La fonction sendRequest sera appelée lorsque le bouton sera cliqué, avec l’argument supplémentaire { username: ’johndoe’ }. La valeur de isMutating sera définie sur true jusqu’à ce que la mutation soit terminée.

De plus, ce nouveau hook résout d’autres problèmes que vous pouvez rencontrer avec les mutations :

  • Mise à jour optimiste de l’UI pendant que les données sont mutées
  • Rétablisement automatique en cas d’échec de la mutation
  • Éviter les éventuels problèmes de concurrence entre useSWR et d’autres mutations de la même ressource
  • Alimenter le cache de useSWR après la fin de la mutation
  • ...

Vous pouvez trouver des références d’API détaillées et des exemples en lisant la documentation ou en parcourant les sections suivantes.

UI Optimiste

L’UI Optimiste est un excellent modèle pour créer des sites web qui semblent rapides et réactifs ; cependant, il peut être difficile à implémenter correctement. SWR 2.0 a ajouté de nouvelles options puissantes pour le rendre plus facile.

Disons que nous avons une API qui ajoute un nouvel élément à une liste de tâches et l’envoie au serveur :

await addNewTodo('New Item')

Dans notre UI, nous utilisons un hook useSWR pour afficher la liste de tâches, avec un bouton "Ajouter un nouvel élément" qui déclenche cette requête et demande à SWR de recharger les données via mutate() :

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Afficher les donées */}</ul>
 
  <button onClick={async () => {
    await addNewTodo('Nouvel Element')
    mutate()
  }}>
    Ajouter un Nouvel Element
  </button>
</>

Cependant, la requête await addNewTodo(...) peut être très lente. Lorsqu’elle est en cours, les utilisateurs voient toujours l’ancienne liste même si nous pouvons déjà savoir à quoi ressemblera la nouvelle liste. Avec la nouvelle option optimisticData, nous pouvons afficher la nouvelle liste de manière optimiste, avant que le serveur ne réponde :

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Afficher les donées */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('Nouvel Element'), {
      optimisticData: [...data, 'Nouvel Element'],
    })
  }}>
    Ajouter un Nouvel Element
  </button>
</>

SWR va immédiatement mettre à jour les data avec la valeur optimisticData, et ensuite envoyer la requête au serveur. Une fois la requête terminée, SWR va revalider la ressource pour s’assurer qu’elle est à jour.

Comme beaucoup d’API, si la requête addNewTodo(...) nous renvoie les dernières données du serveur, nous pouvons directement afficher ce résultat (au lieu de démarrer une nouvelle revalidation) ! Il y a une nouvelle option populateCache pour dire à SWR de mettre à jour les données locales avec la réponse de la mutation :

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Afficher les donées */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('Nouvel Element'), {
      optimisticData: [...data, 'Nouvel Element'],
      populateCache: true,
    })
  }}>
    Ajouter un Nouvel Element
  </button>
</>

En même temps, nous n’avons plus besoin d’une nouvelle revalidation, car les données de réponse proviennent de la source de confiance, nous pouvons désactiver cet revalidation avec l’option revalidate :

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Afficher les donées */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('Nouvel Element'), {
      optimisticData: [...data, 'Nouvel Element'],
      populateCache: true,
      revalidate: false,
    })
  }}>
    Ajouter un Nouvel Element
  </button>
</>

Enfin, si addNewTodo(...) échoue avec une exception, nous pouvons rétablire les données optimistes ([...data, 'Nouvel Element']) que nous venons de définir, en définissant rollbackOnError sur true (qui est également l’option par défaut). Lorsque cela se produit, SWR va rétablir les data à la valeur précédente.

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Afficher les donées */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('Nouvel Element'), {
      optimisticData: [...data, 'Nouvel Element'],
      populateCache: true,
      revalidate: false,
      rollbackOnError: true,
    })
  }}>
    Ajouter un Nouvel Element
  </button>
</>

Toutes ces options sont également disponibles dans le nouveau hook useSWRMutation. Pour en savoir plus, vous pouvez consulter notre documentation. Et voici une démo montrant ce comportement :

UI Optimiste avec rétablisement automatique en cas d’erreur

Multiple Clé de Mutation

L’API global mutate peut maintenant accepter une fonction de filtre, où vous pouvez muter ou revalider des clés spécifiques. Cela sera utile pour, par exemple, l’invalidation de toutes les données mises en cache. Pour en savoir plus, vous pouvez lire Mutate Multiple Keys dans la documentation.

import { mutate } from 'swr'
// Ou depuis le hook si vous avez personnalisé votre fournisseur de cache:
// { mutate } = useSWRConfig()
 
// Mutation d’une seule ressource
mutate(key)
 
// Mutation de plusieurs ressources et effacement du cache (défini sur undefined)
mutate(
  key => typeof key === 'string' && key.startsWith('/api/item?id='),
  undefined,
  { revalidate: false }
)

SWR Outils de Développement

SWRDevTools (opens in a new tab) est une extention de navigateur qui vous aide à débugger votre cache SWR et les résultats de récupération. Consultez notre section devtools pour savoir comment utiliser les outils de développement dans votre application.

Prechargement des Données

Le préchargement des données peut améliorer considérablement l’expérience utilisateur. Si vous savez que la ressource sera utilisée plus tard dans l’application, vous pouvez utiliser la nouvelle API preload pour charger les données plus tôt :

import useSWR, { preload } from 'swr'
 
const fetcher = (url) => fetch(url).then((res) => res.json())
 
// Vous pouvez appeler la fonction preload n’importe où
preload('/api/user', fetcher)
 
function Profile() {
  // Le composant qui utilise les données:
  const { data, error } = useSWR('/api/user', fetcher)
  // ...
}
 
export function Page () {
  return <Profile/>
}

Dans cet exemple, l’API preload est appelée globalement. Cela signifie que que le prechargement des resources commence avant que React ne déclanche le rendu. Et lorsque le composant Profile est rendu, les données peuvent probablement être disponibles. Si ce n’est pas le cas, le hook useSWR réutilisera la requête de prechargement en cours au lieu d’en démarrer une nouvelle.

L’API preload peut aussi être utilisée dans des cas comme le prechargement des données pour une autre page qui sera probablement rendue. Vous pouvez trouver plus d’informations sur le prechargement des données avec SWR ici.

isLoading

isLoading est le nouvel état retourné par useSWR, qui indique si la requête est toujours en cours et qu’aucune donnée n’a encore été chargée. Précédemment, isValidating représentait à la fois l’état de chargement initial et l’état de revalidation. Il fallait donc vérifier si data et error étaient undefined pour déterminer s’il s’agissait de l’état de chargement initial.

Maintenant, c’est plus simple et vous pouvez utiliser directement la valeur isLoading pour afficher un message de chargement :

import useSWR from 'swr'
 
function Profile() {
  const { data, isLoading } = useSWR('/api/user', fetcher)
 
  if (isLoading) return <div>chargement...</div>
  return <div>bonjour {data.name}!</div>
}

Notez que isValidating est toujours présent, vous pouvez donc toujours l’utiliser pour afficher un indicateur de chargement pour les revalidations.

📝

Nous avons ajouté la nouvelle page Comprendre SWR pour décrire comment SWR retourne les valeurs, ce qui inclut la différence entre isValidating et isLoading, et comment les combiner pour améliorer l’expérience utilisateur.

Conservation de l’État Précédent

L’option keepPreviousData est une nouvelle fonctionnalité qui vous permet de conserver les données qui ont été récupérées précédemment. Cela améliore considérablement l’expérience utilisateur lorsque vous récupérez des données en fonction d’actions utilisateur qui se produisent en temps réel, comme avec une fonction de recherche en direct, où la clé de la ressource change :

function Search() {
  const [search, setSearch] = React.useState('');
 
  const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
    keepPreviousData: true
  })
 
  return (
    <div>
      <input
        type="text"
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="Rechercher..."
      />
 
      <div className={isLoading ? "chargement" : ""}>
        {data?.products.map(item => <Product key={item.id} name={item.name} />)}
      </div>
    </div>
  );
}
Convervation des précédent résultat de recherche avec keepPreviousData activable

Allez voir le code sur CodeSandbox (opens in a new tab) et vous pouvez en lire plus à ce sujet ici.

Extension des Configurations

SWRConfig peut maintenant accepter comme valeur une fonction. Lorsque vous avez plusieurs niveaux de <SWRConfig>, le niveau interne reçoit la configuration parent et retourne une nouvelle configuration. Ce changement rend la configuration de SWR plus flexible dans une grande codebase. Vous pouvez trouver plus d’informations ici.

<SWRConfig
  value={parentConfig => ({
    dedupingInterval: parentConfig.dedupingInterval * 5,
    refreshInterval: 100,
  })}
>
  <Page />
</SWRConfig>

Amélioration du Support de React 18

SWR a été mis à jour pour utiliser les API useSyncExternalStore et startTransition de React 18. Celles-ci assurent une plus grande cohérence lors du rendu de l’UI simultanément. Ce changement ne nécessite aucune modification du code utilisateur et tous les développeurs en bénéficieront directement. Shims est inclus pour React 17 et les précédemment versions.

SWR 2.0 et toutes les nouvelles fonctionnalités sont toujours compatibles avec React 16 et 17.

Guide de Migration

La fonction de récupération n’accepte plus plusieurs paramètres

key est passé comme un seul argument.

- useSWR([1, 2, 3], (a, b, c) => {
+ useSWR([1, 2, 3], ([a, b, c]) => {
  assert(a === 1)
  assert(b === 2)
  assert(c === 3)
})

La Mutation globale n’accept plus la fonction getKey

Maintenant, si vous passez une fonction à la mutation globale, elle sera utilisée comme un filtre. Précédemment, vous pouviez passer une fonction qui renvoie une clé à la mutation globale :

- mutate(() => '/api/item') // une fonction qui revoie une clé
+ mutate('/api/item')       // pour transformer la clé, passez-la directement

Nouvelle Propriété Requise keys() pour l’Interface Cache

Quand vous utilisez votre propre implémentation de cache, l’interface Cache demande maintenant une méthode keys() qui retourne toutes les clés de l’objet cache, similaire aux instances JavaScript Map.

interface Cache<Data> {
  get(key: string): Data | undefined
  set(key: string, value: Data): void
  delete(key: string): void
+ keys(): IterableIterator<string>
}

Modification de la Structure Interne du Cache

La structure interne des données du cache est un objet qui contient tous les états actuels.

- assert(cache.get(key) === data)
+ assert(cache.get(key) === { data, error, isValidating })
 
// getter
- cache.get(key)
+ cache.get(key)?.data
 
// setter
- cache.set(key, data)
+ cache.set(key, { ...cache.get(key), data })
🚨

Vous ne devriez pas écrire directement dans le cache, cela peut causer un comportement inprévisible.

SWRConfig.default est renommé SWRConfig.defaultValue

SWRConfig.defaultValue est la propriété pour accéder à la configuration SWR par défaut.

- SWRConfig.default
+ SWRConfig.defaultValue

Le Type InfiniteFetcher est renommé SWRInfiniteFetcher

- import type { InfiniteFetcher } from 'swr/infinite'
+ import type { SWRInfiniteFetcher } from 'swr/infinite'

Evitez Suspense sur le Serveur

Si vous voulez utilisez suspense: true avec SWR côté serveur, incluant le pré-rendu dans Next.js, vous devez fournir des données initiales via fallbackData ou fallback. Aujourd’hui, cela signifie que vous ne pouvez pas utiliser Suspense pour récupérer des données côté serveur. Vos deux autres options sont de faire de la récupération de données côté client ou de faire récupérer les données par votre framework (comme le fait getStaticProps dans Next.js).

ES2018 comme cible de Build

Si vous voulez supporter IE 11, vous devez cibler ES5 dans votre framework ou votre bundler. Ce changement a permis une amélioration des performances sur le SSR, et garde la taille du bundle petite.

Changelog

Lisez le Changelog complet sur GitHub (opens in a new tab).

Le Future & Merci !

Avec la sortie de Next.js 13 (opens in a new tab), nous voyons beaucoup de nouvelles choses passionnantes ainsi que des changements de paradigmes dans l’écosystème React : React Server Components (opens in a new tab), streaming SSR, async components (opens in a new tab), et le use hook (opens in a new tab). Beaucoup d’entre eux sont liés à la récupération de données, et certains d’entre eux ont des cas d’utilisation qui se chevauchent avec SWR.

Cependant, le bût du projet SWR reste le même. Nous voulons que cette librairie soit légère, indépendante de tout framework, et un peu opinionated (i.e. revalider lors du focus). Au lieu d’essayer d’être une solution standard, nous voulons nous concentrer sur les innovations qui rendent l’UX meilleure. En attendant, nous faisons également des recherches sur la façon d’améliorer SWR avec ces nouvelles capacités de React.

Nous voulons aussi remercier tous les 143 (opens in a new tab) contributeurs (+ 106 (opens in a new tab) contributeurs de la documentation), ainsi que ceux qui nous ont aidé ou donné des retours. Un merci spécial à Toru Kobayashi (opens in a new tab) pour tout son travail sur les outils développeur et la documentation - nous n’aurions pas pu le faire sans toi !